Vue中实现父子组件双向数据流的三种方案分享
作者:FEF前端团队
1. 背景
通常情况下,父子组件的通信都是单向的,或父组件使用props
向子组件传递数据,或子组件使用emit
函数向父组件传递数据。
但在有些场景下,免不了需要父子组件之间进行双向数据流,从而让我们的业务代码更加简洁。很常见的场景之一:父组件引入了一个子组件,子组件是个dialog
弹窗,在父组件可以点击某个按钮显示dialog
,但是dialog
组件的关闭操作是在组件里面的。这个时候使用双向数据流管理dialog
组件的显隐状态,不仅让代码显得优雅,还能少写不少业务。
那我们能不能直接在子组件修改props
来实现上述功能呢?当然是不行的,因为在Vue
中props
属性是只读的。官方文档给出的解释如下:
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。本文将尝试讲解Vue中常用的几种双向数据流的使用。
2. 双向数据流
2.1. 什么是双向数据流
简而言之,双向数据流就是model
的更新会触发view
的更新,view
的更新会触发model
的更新,它们的作用是相互的。
tips: 这里只讨论父子组件传值时的双向数据流,对框架数据的双向数据流不做展开。
2.2. Vue提供的解决方案
2.2.1. 方案一:v-model
父组件代码如下:
<template> <div class="container"> <a-button type="primary" @click="handleClick">改变子组件的状态</a-button> <p>父组件状态:{{ message }}</p> <!-- 子组件 --> <child-view v-model="message"></child-view> <!-- 也可以使用多个v-model,子组件里面用props正常接收就可以了 --> <!-- <child-view v-model:message="message" v-model:msg="msg"></child-view> --> </div> </template> <script> import ChildView from '@/components/ChildView.vue' export default { name: 'HomeView', components: { ChildView }, data () { return { message: '' } }, methods: { handleClick () { this.message = '我是用v-model修改的状态' } } } </script>
子组件代码如下:
<template> <div> <a-button type="primary" @click="handleClick">改变父组件的状态</a-button> <p>子组件状态:{{ value }}</p> </div> </template> <script> export default { props: { // 这里框架默认属性名就是value value: { type: String, default: '' }, // 接收多个props message: { type: String, default: '' }, msg: { type: String, default: '' } }, methods: { handleClick () { // 这里框架默认事件名就是input // 发送的数据会被父级v-model="message"接收到,再被value=message传回来 this.$emit('input', '我是在子组件里面修改父组件v-model的值') } } } </script>
自定义v-model
的props
属性:
Vue 允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
<template> <div> <a-button type="primary" @click="handleClick">改变父组件的状态</a-button> <p>子组件状态:{{ msg }}</p> </div> </template> <script> export default { // 自定义prop名称和事件 model: { prop: 'msg', event: 'update' }, props: { msg: { type: String, default: '' } }, methods: { handleClick () { this.$emit('update', '我是在子组件里面修改父组件v-model的值') } } } </script>
浏览器运行效果:
2.2.2. 方案二:sync修饰符
父组件代码如下:
<template> <div class="container"> <a-button type="primary" @click="handleClick">改变子组件的状态</a-button> <p>父组件状态:{{ message }}</p> <child-view :msg.sync="message"></child-view> </div> </template> <script> import ChildView from '@/components/ChildView.vue' export default { name: 'HomeView', components: { ChildView }, data () { return { message: '' } }, methods: { handleClick () { this.message = '十里平湖霜满天' } } } </script>
子组件代码如下:
<template> <div> <a-button type="primary" @click="handleClick">改变父组件的状态</a-button> <p>子组件状态:{{ msg }}</p> </div> </template> <script> export default { props: { msg: String }, methods: { handleClick () { this.$emit('update:msg', '寸寸青丝愁华年') } } } </script>
浏览器运行效果:
2.2.3. 方案三:通过JS引用类型特性绕过props
父组件代码如下:
<template> <div class="container"> <a-button type="primary" @click="handleClick">改变子组件的状态</a-button> <p>父组件状态:{{ config.message }}</p> <child-view :config="config"></child-view> </div> </template> <script> import ChildView from '@/components/ChildView.vue' export default { name: 'HomeView', components: { ChildView }, data () { return { config: { message: '' } } }, methods: { handleClick () { this.config.message = '我是用v-model修改的状态' } } } </script>
子组件代码如下:
通过JS引用类型特性绕过props
运行效果和v-model
一样,这里就不贴图了。
3. 总结
Vue
父子组件通信,都是单向的,在需要双向通信的时候,我们有三种方式达到目的:
- 使用
v-model
- 使用
sync
修饰符 - 通过JS引用类型特性绕过
props
单向数据流
以上就是Vue中实现父子组件双向数据流的三种方案分享的详细内容,更多关于Vue实现父子组件双向数据流的资料请关注脚本之家其它相关文章!