前端 | 自定义组件 v-model:Vue 如何实现双向绑定

  • A+
所属分类:Web前端
摘要

v-model 是 Vue 中一个常用的指令,常用于表单中的数据绑定。如下基本用法想必大家都很熟悉,data 中的 checked 属性的值就会随着多选框的状态实时变化。

v-model 是 Vue 中一个常用的指令,常用于表单中的数据绑定。如下基本用法想必大家都很熟悉,data 中的 checked 属性的值就会随着多选框的状态实时变化。

<el-checkbox v-model="checked" /> 

但你或许听说过,Vue 组件之间是“单向数据流”,即通过 props 从父组件向子组件单向传递数据。那么,v-model 的“双向绑定”效果是如何实现的呢?

自定义组件实现 v-model

首先来看一个实际应用的例子,需求如下:在创建试卷页面 paperCreate 中,通过选择题目组件 questionSelect 查询并选择题目组成试卷,效果如下图所示。

前端 | 自定义组件 v-model:Vue 如何实现双向绑定

为了方便使用,希望能在选择题目组件上使用 v-model 指令进行双向绑定:

<!-- 选择题目组件 --> <question-select v-model="formData.questions" />  <!-- 显示已选择题目 --> <div v-for="item in formData.questions" :key="item.id" >     <question :question="item" /> </div> 

value 属性和 input 事件

自定义支持 v-model 的组件只需要满足两个条件:

  • value 属性作为传入数据
  • 数据改变时,将新数据作为 payload,向上 emit 一个 input 事件

就是这么简单,相关代码为:

export default {   props: {     value: Array,	// 类型根据实际需要   },   methods: {     handelChange() {       this.$emit("input", newValue);     },   }, }; 

如果不想使用默认的 value 属性和 input 事件,也可以通过 model 对象自定义相应属性和事件:

model: {   prop: 'checked',   event: 'change', }, 

单向数据流

需要注意的是,prop 传值是单向的,即父组件中的数据改变会反映到子组件中,但在子组件内部不能主动改变 prop 的值。具体来说就是,子组件中不能使用类似于 v-model="value" 之类试图改变 value 值的操作。

因此表格中的 checkbox,也只单向传入 value 来控制是否选中的状态,然后在选中状态改变时手动处理改变后的结果。

  • 对于表格中的一行(即一道题目),如果传入的 value 数组(即已选中的题目列表)中包含它的 id,则显示为选中状态
  • 不能直接改变原数组 value,直接构造新列表传出:
    • 如果新增了一个选项,传原列表 + 所选对象
    • 如果取消了一个选项,传原列表 - 所选对象

(PS:组件库中提供的 el-checkbox 实际上也是另一个自定义组件,可以同理分析)

<el-table :data="questionList">   <el-table-column>     <template slot-scope="scope">       <el-checkbox         :value="selectedIds.includes(scope.row.id)"         @change="(value) => handelChange(scope.row, value)"       />     </template>   </el-table-column>   <!-- 其他属性 --> </el-table> 
props: {   value: Array, }, computed: {   selectedIds() {     return this.value.map((item) => item.id);   }, }, methods: {   handelChange(item, checked) {     if (checked) {       this.$emit("input", [...this.value, item]);     } else {       this.$emit(         "input",         this.value.filter((element) => element.id != item.id)       );     }   }, }, 

具体写法与组件库的具体实现有关,简单说明一下此处的 element 语法:

  • scope.row 为 el-table 表格当前行对应的对象
  • el-checkbox 的 change 事件的负载为复选框点击后的新值

然后就结束了,父组件使用选择题目组件时就能正常使用 v-model 了。本例中绑定的数据是完整的题目列表,原因是需要在页面中显示已选择题目的具体信息;如果只需要 id 数据(例如 select 那样的组件),则 emit 时只传 id 列表即可,写法完全一致。

双向绑定

通过上面的例子想必你已经看出来了,v-model 指令的“双向绑定”实际上是一个语法糖:它将父组件的数据通过 prop 传入子组件,再监听子组件的输入事件来更新父组件中的数据,从而实现双向绑定的效果。

前端 | 自定义组件 v-model:Vue 如何实现双向绑定

结语&参考资料

以上是个人对 Vue 中 v-model 指令的一些理解与思考,希望能给你提供帮助。如果有问题或疏漏之处,欢迎在评论中讨论与指正。

我将继续在个人博客中更新自己的学习笔记,以前端技术(Vue框架)为主,感兴趣的话欢迎关注!

参考资料:Vue 官方文档