如何将我的数据存储在自定义 Vue 组件而不是根 Vue 实例中?

Posted

技术标签:

【中文标题】如何将我的数据存储在自定义 Vue 组件而不是根 Vue 实例中?【英文标题】:How do I store my data in a custom Vue component instead of the root Vue instance? 【发布时间】:2019-10-15 11:48:57 【问题描述】:

我在 Vue 中创建了一个自定义复选框组件,它可以很好地处理存储在根实例中的数据。我计划在各种情况下重用这个组件(以及我正在构建的许多其他组件)。我不想每次使用组件时都必须更新或编辑根 Vue 实例,并且只想将数据存储在组件本身中。已选中/未选中的布尔值需要是响应式的。

我尝试过使用计算值,但也无法让它工作。如果需要,我愿意使用它。

(此版本无效)

<body>
  <script src="https://unpkg.com/vue@2.6.10"></script>
  <div id="app">
    <checkbox-item v-model="checkData">Active</checkbox-item>
     checkData 
  </div>
</body>

</html>

<script>
  Vue.component('checkbox-item', 
    template: `
          <label class="checkbox-item">
            <input type="checkbox" :checked="value"
                   @change="$emit('input', $event.target.checked)"
                   class="checkbox-input">
            <span class="checkbox-label">
              <slot></slot>
            </span>
          </label>
        `,
    data: function() 
      return 
        checkData: null
      
    ,
    props: ['value']
  )
  new Vue(
    el: '#app',
  )
</script>

(此版本有效,但我再次需要数据不在根实例中)

<body>
  <script src="https://unpkg.com/vue@2.6.10"></script>
  <div id="app">
    <checkbox-item v-model="checkData">Active</checkbox-item>
     checkData 
  </div>
</body>

<script>
  Vue.component('checkbox-item', 
    template: `
          <label class="checkbox-item">
            <input type="checkbox" :checked="value"
                   @change="$emit('input', $event.target.checked)"
                   class="checkbox-input">
            <span class="checkbox-label">
              <slot></slot>
            </span>
          </label>
        `,
    props: ['value']
  )
  new Vue(
    el: '#app',
    data: 
      checkData: null
    
  )
</script>

我得到的错误是:

[Vue 警告]:属性或方法“checkData”未在实例上定义,但在渲染期间被引用。通过初始化该属性,确保此属性是反应性的,无论是在数据选项中,还是对于基于类的组件。请参阅:https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties。

并且checkData 不像在工作示例中那样具有反应性。

编辑:好的,这就是有效的!我肯定会考虑使用 SFC 和其他代码组织方法,但现在它仍然在一个 html 文件中。有没有人认为这从长远来看是行不通的?

  <body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <div id="app">
        <checkbox-item></checkbox-item>
    </div>
  </body>
</html>

<script>
  Vue.component('checkbox-item', 
    template: `
      <label class="checkbox-item">
        <input type="checkbox" v-model="checkData"
               class="checkbox-input">
        <span class="checkbox-label">
          <slot>Active: checkData</slot>
        </span>
      </label>
    `,
    data: function()
      return 
        checkData: this.checked
      
    ,
  )
  new Vue(
    el: '#app',
  )
</script>

【问题讨论】:

我强烈建议看看 VueMastery 的介绍和高级组件课程。了解 v-on="$listeners" 和 v-bind="$attrs" 会对你有很大帮助。他们还深入探讨了使用槽和作用域槽的组合。最后,VueLand Discord 是获得帮助的绝佳免费资源。 【参考方案1】:

第一个示例不起作用的原因是您的根 Vue 组件无权访问复选框的数据。

<html>
<!-- Your existing nonfunctional example -->
<body>
  <script src="https://unpkg.com/vue@2.6.10"></script>
  <div id="app">
    <!-- Both v-model and  checkData 
      are asking about the root vue's checkData,
      but you took that off. This is why you have an error -->
    <checkbox-item v-model="checkData">Active</checkbox-item>
     checkData 
  </div>
</body>

</html>

非功能示例中发生了什么?

当您在非功能示例中说 v-model="checkData" 时,它告诉父(根)Vue 组件在其本地范围内查找 checkData 以及 v-bind:value="checkData"v-on:input="checkData = $event.target.value"

使您的复选框项目可重复使用

您的组件可以按原样重复使用。

我对您的checkbox-item 所做的唯一更改是在input 元素上传递v-on="$listeners"v-bind="$attrs",并删除道具中显式的“值”。

对于任何低级、可重用的 UI 组件,您都需要输入和输出数据。使用 v-model 并将数据存储在父组件中实际上是您想要做的。将数据存储在根组件中感觉很笨拙,因此通常会有中级组件。

为了证明这一点,我列出了可以高兴或悲伤的书呆子,并使用您的复选框来更新他们的状态。

在 Root Vue 组件中的使用
<body>
  <script src="https://unpkg.com/vue@2.6.10"></script>
  <div id="app">
    <!-- If you wanted to take in data,
        you would pass in the nerds array as a Prop
        into nerd-list _from_ the root Vue instance -->
    <nerd-list/> 
  </div>
</body>

<script>
  Vue.component('nerd-list', 
   template: '<div>
     <h1>List of nerds and if they are happy</h1>
     <div v-for="nerd in nerds" :key="nerd.id">
        nerd.name 
       <checkbox-item v-model="nerd.happy">Happy</checkbox-item>
     </div>
   </div>',
  data() 
    return 
      nerds: [
         name: 'Jess', happy: true, id: 1 ,
         name: 'Tiffany', happy: true, id: 2 ,
      ]
    ;
  ,
);

  Vue.component('checkbox-item', 
    template: `
          <label class="checkbox-item">
            <input type="checkbox" :checked="value"
                   @change="$emit('input', $event.target.checked)"
                   class="checkbox-input">
            <span class="checkbox-label">
              <slot></slot>
            </span>
          </label>
        `,
    props: ['value']
  )
  new Vue(
    el: '#app',
    data: , // any data in here could be used to pass props into the nerd-list.
  )
</script>

最后,如您所知,将所有这些都写在 javascript 文件中可能看起来有点麻烦。我强烈推荐使用 Vue CLI 和 Vue SFC 组件以获得更好的体验。

【讨论】:

非常感谢您的详细回复和有趣的例子! ;-) 没问题@TiffanyDevlin-Drye!如果答案正确,请务必接受。

以上是关于如何将我的数据存储在自定义 Vue 组件而不是根 Vue 实例中?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建 Vue 3 自定义元素,包括子组件样式?

vue3的根节点

如何仅为我的自定义 vue 组件包含外部 css 文件?

如何使用 jsDoc 在自定义组件中记录此关键字?

Azure 自定义视觉:是不是可以在自定义视觉环境中存储图像元数据(不是标签)?

如何在 vuex 的挂载函数中访问存储数据