vue2 与 vue3 之组件间数据双向绑定的区别
「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
1、vue2 中的组件间数据双向绑定
① 对 vue2 组件间数据双向绑定进行简单的说明
-
在 Vue 2.0 发布后,开发者使用 v-model 指令时必须使用名为 value 的 prop。如果开发者出于不同的目的需要使用其他的 prop,就不得不使用 v-bind.sync。此外,由于v-model 和 value 之间的这种硬编码关系的原因,产生了如何处理原生元素和自定义元素的问题。
-
在 Vue 2.2 中,引入了 model 组件选项,允许组件自定义用于 v-model 的 prop 和事件。但是,这仍然只允许在组件上使用一个 v-model。
-
在 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
-
在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,官网建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,可以通过下面的方式将分配新 value 的意图传达给父级:
this.$emit('update:title', newValue)
-
然后父组件可以在需要时监听该事件,并更新本地的 data property。例如:
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
-
为了方便起见,我们可以使用 .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>
需要注意两点:
- props 中从父组件传递过来的值,要将其进行分开书写,因为我 Parent.vue 父组件中传递的时候是分开传值的,并没有将一个对象一起传递过来
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>
④ 效果图
2、vue3 中的组件间数据双向绑定
① 对 vue3 中的变动做一下简单的说明
-
在 vue2 中的话,可以通过 v-bind.sync 方法进行组件间的数据双向绑定,但是 vue3 则将这个
.sync
修饰符去掉了。 -
在 vue3 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:
<ChildComponent v-model="pageTitle" /> <!-- 是以下的简写: --> <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>
-
若需要更改 model 的名称,现在我们可以为 v-model 传递一个参数,以作为组件内 model 选项的替代:
<ChildComponent v-model:title="pageTitle" /> <!-- 是以下的简写: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
-
这也可以作为 .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 方法,才可以实现正常的数据双向绑定。
③ 效果图
3、总结
-
在子组件中我们不能直接修改父组件传过来的 prop 值,一般情况下通过 $emit 传递事件,然后在父组件中监听这个事件,再在事件中修改这个 prop 值。v-bind.sync 可以理解为是这种方式的语法糖。
-
在父组件中给子组件标签设置 v-bind.sync 可以实现 props 的双向绑定
-
一个组件可以多个属性用 .sync 修饰符,可以同时"双向绑定多个“prop”,而并不像 v-model 那样,一个组件只能有一个。
-
v-model 针对更多的是最终操作结果,是双向绑定的结果,是 value,是一种 change 操作。
-
v-bind.sync 针对更多的是各种各样的状态,是状态的互相传递,是 status,是一种 update 操作。标签使用的方法是 input 事件。
转载自:https://juejin.cn/post/7056589377090093093