Catalyst

Vue3 的 v-model 和绑定

最近在试图绑定一套自己的组件库,在实现input上出了点问题,查了一下实现似乎和以前不一样了。

Vue3 v-model 的变化

根据 官方文档 的说法:

  • 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
    • prop:value -> modelValue
    • event:input -> update:modelValue
  • 非兼容v-bind.sync 修饰符和组件的 model 选项已移除,可用 v-model 作为代替;
  • 新增:现在可以在同一个组件上使用多个 v-model 进行双向绑定;
  • 新增:现在可以自定义 v-model 修饰符。

v-model实际上是一个语法糖,在vue3中:

<ChildComponent v-model="pageTitle" />
<!-- 其实是 -->
<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>

现在我们可以在v-model上添加参数来改变作响应的变量名。比如说

<ChildComponent v-model:title="pageTitle" />
<!-- 将内部绑定的变量名指向 title -->
<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
/>

这样也可以在同一个组件上使用多个v-model

绑定组件

回到开头,那么如何给 input 组件绑定呢?继续 抄答案

这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 modelValue 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 update:modelValue 事件抛出
app.component("custom-input", {
  props: ["modelValue"],
  emits: ["update:modelValue"],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  `,
});

一个完整的测试:

const App = {
  setup() {
    const customValue = Vue.ref("");
    return { customValue };
  },
};

var app = Vue.createApp(App);

app.component("CustomInput", {
  props: ["modelValue"],
  emits: ["update:modelValue"],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  `,
});
app.mount("#app");

利用原有v-model的套娃方法:

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="app">
      <custom-input v-model="customValue"></custom-input>
      <div>{{customValue}}</div>
    </div>
  </body>
  <script src="https://unpkg.com/vue@next"></script>
  <script>
    const App = {
      setup() {
        const customValue = Vue.ref("Test");
        return { customValue };
      },
    };

    var app = Vue.createApp(App);

    app.component("CustomInput", {
      props: ["modelValue"],
      computed: {
        model: {
          get() {
            return this.modelValue;
          },
          set(val) {
            this.$emit("update:modelValue", val);
          },
        },
      },
      template: `<input v-model="model">`,
    });
    app.mount("#app");
  </script>
</html>