转载来自博客园:https://www.cnblogs.com/skuld-yi/p/15823949.html

自定义组件 使用v-model 指令进行双向绑定

  • 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 传入子组件,再监听子组件的输入事件来更新父组件中的数据,从而实现双向绑定的效果。 alt

.sync 修饰符

.sync修饰符可以实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值

一般情况下,想要实现父子组件间值得传递,通常使用得时props和自定义事件$emit.

其中,父组件通过props 将值传给子组件,子组件再通过$emit 将值传给父组件,父组件通过事件监听获取子组件传过来得值。

如果要简化这里得代码,可以使用.sync修饰符,实际上就是一个语法糖

Father.vue

    
        <template>
          <div class="app">
             {{dataApp}}
            <hr>
            <Child :money.sync="dataApp"/>
            // <Child :money="dataApp" v-on:update:money="dataApp = $event"/>  //两句作用等同
          </div>
        </template>
        import Child from "./Child.vue";
        export default {
          data() {
            return { dataApp: 10000 };
          },
          components: { Child }
        };

Child.vue


          <template>
            <div class="child">
              {{money}}
              <button @click="$emit('update:money', money-100)">
              </button>
            </div>
          </template>

          export default {
            props: ["money"]
          };

上面例子中,父组件传了一个参数 money 给子组件,子组件通过$emit修改 money 后,将值同步到父组件。

这里

          <Child :money="dataApp" v-on:update:money="dataApp = $event"/>

就相当于

<Child :money.sync="dataApp"/> 。
  • v-model 的本质就是语法糖,但是运行时也做了一些优化
  • v-model 即可以支持原生表单元素,也可以支持组件。在组件的实现中,我们时可以配置子组件接收的prop名称,一几派发的事件名称