likes
comments
collection

vue2 与 vue3 之组件间数据双向绑定的区别

作者站长头像
站长
· 阅读数 33

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

1、vue2 中的组件间数据双向绑定

① 对 vue2 组件间数据双向绑定进行简单的说明

  1. 在 Vue 2.0 发布后,开发者使用 v-model 指令时必须使用名为 value 的 prop。如果开发者出于不同的目的需要使用其他的 prop,就不得不使用 v-bind.sync。此外,由于v-model 和 value 之间的这种硬编码关系的原因,产生了如何处理原生元素和自定义元素的问题。

  2. 在 Vue 2.2 中,引入了 model 组件选项,允许组件自定义用于 v-model 的 prop 和事件。但是,这仍然只允许在组件上使用一个 v-model。

  3. 在 2.x 中,在组件上使用 v-model 相当于绑定 value prop 并触发 input 事件。

  • 体验:

    <ChildComponent v-model="pageTitle" />
    
    <!-- 是以下的简写: -->
    <ChildComponent :value="pageTitle" @input="pageTitle = $event" />
    
    • 如果想要更改 prop 或事件名称,则需要在 ChildComponent 组件中添加 model 选项:
    <!-- ParentComponent.vue 父组件中的内容 -->
    <ChildComponent v-model="pageTitle" />
    
    // ---------------------------------------------------------------------------------------
    
    <!-- ChildComponent.vue 子组件中的内容 -->
    <script>
    export default {
      model: {
        prop: 'title',
        event: 'change'
      },
      props: {
        // 这将允许 `value` 属性用于其他用途
        value: String,
        // 使用 `title` 代替 `value` 作为 model 的 prop
        title: {
          type: String,
          default: 'Default title'
        }
      }
    }
    </script>
    
    • 所以,在这个例子中 v-model 是以下的简写:
    <ChildComponent :title="pageTitle" @change="pageTitle = $event" />
    

② v-bind.sync

  1. 在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,官网建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,可以通过下面的方式将分配新 value 的意图传达给父级:

    this.$emit('update:title', newValue)
    
  2. 然后父组件可以在需要时监听该事件,并更新本地的 data property。例如:

    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    
  3. 为了方便起见,我们可以使用 .sync 修饰符来缩写,如下所示:

    <ChildComponent :title.sync="pageTitle" />
    

③ 上代码实践

  • Children.vue 子组件代码:

    对下面子组件的一点注意事项做一个说明:各位可能发现我的 props 传值中的 age 参数,设置的是 [Number, String],这是因为在从父组件传递过来 age 的时候,它本身是一个 Number 类型的值,但是在 input 表单中,会被自动转换为 String 类型的值,如果不设置其 [Number, String] 两个数据类型的话,运行的时候控制台会报错

    <template>
      <div>
        <button @click="updateView">更新视图</button>
        <input type="text" :value="uName" @input="uNameViewUpdate" ref="uName" />
        <input type="text" :value="age" @input="ageViewUpdate" ref="age" />
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        // propsObj 对象里的属性分开写
        uName: String,
        age: [Number, String]
      },
      methods: {
        updateView() {
          this.$emit('update:uName', '新标题');
          this.$emit('update:age', '新标题');
        },
        uNameViewUpdate() {
          const uNameValue = this.$refs.uName.value;
          this.$emit('update:uName', uNameValue);
        },
        ageViewUpdate() {
          const ageValue = this.$refs.age.value;
          this.$emit('update:age', ageValue);
        }
      }
    };
    </script>
    
    <style scoped></style>
    

    需要注意两点:

    1. props 中从父组件传递过来的值,要将其进行分开书写,因为我 Parent.vue 父组件中传递的时候是分开传值的,并没有将一个对象一起传递过来
    2. this.$emit('update:uName', '新标题'); 这个方法中,update 后面的参数必须与 props 里面定义的参数保持一致。
  • Parent.vue 父组件代码

    <template>
      <div id="app">
        <h3>{{ propsObj.uName }}</h3>
        <h3>{{ propsObj.age }}</h3>
        <Children v-bind:uName.sync="propsObj.uName" v-bind:age.sync="propsObj.age" />
      </div>
    </template>
    
    <script>
    import Children from './components/Children.vue';
    
    export default {
      name: 'App',
      components: {
        Children
      },
      data() {
        return {
          propsObj: {
            uName: 'liangshuang',
            age: 21
          }
        };
      }
    };
    </script>
    
    <style scope></style>
    

④ 效果图

vue2 与 vue3 之组件间数据双向绑定的区别

2、vue3 中的组件间数据双向绑定

① 对 vue3 中的变动做一下简单的说明

  1. 在 vue2 中的话,可以通过 v-bind.sync 方法进行组件间的数据双向绑定,但是 vue3 则将这个 .sync 修饰符去掉了。

  2. 在 vue3 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:

    <ChildComponent v-model="pageTitle" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>
    
  3. 若需要更改 model 的名称,现在我们可以为 v-model 传递一个参数,以作为组件内 model 选项的替代:

    <ChildComponent v-model:title="pageTitle" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    

    vue2 与 vue3 之组件间数据双向绑定的区别

  4. 这也可以作为 .sync 修饰符的替代,而且允许我们在自定义组件上使用多个 v-model。

    <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent
      :title="pageTitle"
      @update:title="pageTitle = $event"
      :content="pageContent"
      @update:content="pageContent = $event"
    />
    

② 上代码实践

其他的也就不说了,直接上代码吧,可以按照我的模版实际操作一下。

  • Children.vue 子组件的代码:

    <template>
      <div>
        <input type="text" :value="inputValue.value" @input="updateValue" />
      </div>
    </template>
    
    <script>
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'HelloWorld',
      props: {
        modelValue: String
      },
      setup(props, context) {
        const inputValue = {
          value: props.modelValue || ''
        };
    
        const updateValue = (e) => {
          const targetValue = e.target.value;
          inputValue.value = targetValue;
          context.emit('update:modelValue', targetValue);
        };
    
        return {
          inputValue,
          updateValue
        };
      }
    });
    </script>
    
    <style scoped></style>
    

    需要注意一点:context.emit('update:modelValue', targetValue) 中,update 后面的参数名,必须保持与 props 中命名的一样,这里我命名的是 modelValue;第二个参数 targetValue 则是传递给父组件进行双向数据绑定的数据,即 input 输入框中的内容

  • Parent.vue 父组件的代码:

    <template>
      <div>
        <h2>{{ propsValue }}</h2>
        <Children v-model="propsValue" />
      </div>
    </template>
    
    <script>
    import { defineComponent, ref } from 'vue';
    
    import Children from './components/Children.vue';
    
    export default defineComponent({
      name: 'App',
      components: {
        Children
      },
      setup(props) {
        const propsValue = ref('liangshuang');
    
        return {
          propsValue
        };
      }
    });
    </script>
    
    <style scope></style>
    

    需要注意一点: 父组件中必须要引入 ref 方法,才可以实现正常的数据双向绑定。

③ 效果图

vue2 与 vue3 之组件间数据双向绑定的区别

3、总结

  1. 在子组件中我们不能直接修改父组件传过来的 prop 值,一般情况下通过 $emit 传递事件,然后在父组件中监听这个事件,再在事件中修改这个 prop 值。v-bind.sync 可以理解为是这种方式的语法糖。

  2. 在父组件中给子组件标签设置 v-bind.sync 可以实现 props 的双向绑定

  3. 一个组件可以多个属性用 .sync 修饰符,可以同时"双向绑定多个“prop”,而并不像 v-model 那样,一个组件只能有一个。

  4. v-model 针对更多的是最终操作结果,是双向绑定的结果,是 value,是一种 change 操作。

  5. v-bind.sync 针对更多的是各种各样的状态,是状态的互相传递,是 status,是一种 update 操作。标签使用的方法是 input 事件。