前言: 之前写过一篇文章《在不同场景下Vue组件间的数据交流》,但现在来看,其中关于“父子组件通信”的介绍仍有诸多缺漏或者不当之处, 正好这几天学习了关于用sync修饰符做父子组件数据双向绑定的的用法, 于是决定写一篇文章, 再次总结下“Vue中的父子组件通信”。

前面提示:本文文字略少,代码略多

父子组件通讯,可分为两种情况:

1. 父组件向子组件中传递数据
2. 子组件向父组件中传递数据

一般情况下, 1中情况可通过props解决数据传递的问题, 这里就不多赘述了。

子组件向父组件中传递数据

主要谈谈2中情景的实现,有三种方式:

一. 通过props,父组件向子组件中传递数据和改变数据的函数,通过在子组件中调用父组件传过来的函数,达到更新父组件数据(向父组件传递数据)的作用(子组件中需要有相应的响应事件)
. 通过在子组件中触发一个 自定义事件(vm.$emit),将数据作为vm.$emit方法的参数,回传给父组件用v-on:[自定义事件]监听的函数
.通过ref对子组件做标记,父组件可以通过vm.$refs.[子组件的ref].[子组件的属性/方法]这种方式直接取得子组件的数据

下面我将一 一展示

一. 通过props从父向子组件传递函数,调用函数改变父组件数据

这里就不做代码展示了
一来是因为相对比较简单
二来是因为这种方式显然不是Vue中的最佳实践(在react中倒比较常见)
想要看代码的话可以看这里:《【Vue】浅谈Vue不同场景下组件间的数据交流》http://www.cnblogs.com/penghuwan/p/7286912.html#_label1 (在兄弟组件的数据交流那一节)

二.  通过自定义事件从子组件向父组件中传递数据

我们可以在子组件中通过$emit(event, [...参数])触发一个自定义的事件,这样,父组件可以在使用子组件的地方直接用 v-on来监听子组件触发的事件, 并且可以在监听函数中依次取得所有从子组件传来的参数

例如:
在子组件中某个部分写入:

this.emit('eventYouDefined', arg);

然后你就可以在父组件的子组件模板里监听:
// 这里是父组件的Template:

<Son  v-on: eventYouDefined = "functionYours" />

下面是一个实例

父组件

<template><div id="father"><div>我是父组件,我接受到了:{{ text || '暂无数据'  }}<son v-on:sendData='getSonText'></son></div></div>
</template><script>
import son from './son.vue'
export default {data: function () {return {text: ''}},components: {son: son},methods: {getSonText (text) {this.text = text}}
}</script><style scoped>
#father div {padding: 10px;margin: 10px;border: 1px solid grey;overflow: hidden;
}
</style>

子组件:

<template><div><p>我是子组件,我所拥有的数据: {{ text }}</p><button @click="sendData">发送数据</button></div>
</template><script>
export default {data () {return {text: '来自子组件的数据'}},methods: {sendData () {this.$emit('sendData', this.text)}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>button { float: left }
</style>

在点击子组件中的“发送数据”按钮前, 父组件还没有接受到数据(text为空字符串), 则通过  {{ text || '暂无数据'  }}将显示默认文本:‘暂无数据’


点击“发送数据”按钮后:

因为sendData自定义事件被触发,通过

this.$emit('sendData', this.text)   //此处的this指向子组件实例)

子组件的text数据被父组件中:

 <son v-on:sendData='getSonText'></son>

中的getSonText函数作为参数接传参受到, 从而完成了从子组件向父组件中的传参过程

三. 通过ref属性在父组件中直接取得子组件的数据(data)

对于我们上面讲的一和二的处理情景来说,有个局限性就是它们都需要以事件机制为基础(无论是像click那样的原生事件还是自定义事件),而在事件发生的时候才能调用函数将数据传递过来

但如果子组件里没有类似“按钮”的东西,因而无法制造原生事件,同时也没办法找到一个触发自定义事件的时机的时候,怎么从子组件向父组件传递数据呢??

这个时候, 我们就只能从父组件中“直接取”子组件的数据了,借助ref属性

ref是我们经常用到的Vue属性,利用它可以简单方便地从本组件的template中取得DOM实例,而实际上,如果你在父组件中为子组件设置ref的话, 就可以直接通过vm.$refs.[子组件的ref].[子组件的属性]去拿到数据啦,例如:

父组件:

<template><div id="father"><div>我是父组件,我接受到了:{{ text || '暂无数据'  }}<button @click="getSonText()">接受数据</button><son ref='son'></son></div></div>
</template><script>
import son from './son.vue'
export default {data: function () {return {text: ''}},components: {son: son},methods: {getSonText () {this.text = this.$refs.son.text}}
}</script><style scoped>
#father div {padding: 10px;margin: 10px;border: 1px solid grey;overflow: hidden;
}
</style>

子组件:

<template><div><p>我是子组件,我所拥有的数据: {{ text }}</p></div>
</template><script>
export default {data () {return {text: '来自子组件的数据'}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>button { float: left }
</style>

demo:
尚未点击“接受数据”按钮前:

点击接受数据按钮后:

通过sync实现数据双向绑定, 从而同步父子组件数据

通过以上三种方式, 我想你应该能解决绝大多数父子组件通信的场景了,但让我们再仔细考虑一下上面的通信场景,就会发现它们还可能存在的问题:

从子组件向父组件传递数据时,父子组件中的数据仍不是每时每刻都同步的

在某些特殊的需求场景下,我们可能会希望父子组件中的数据时刻保持同步, 这时候你可能会像下面这样做:

这是父组件中的template:

<son :foo="bar" v-on:update="val => bar = val"></son>

在子组件中, 我们通过props声明的方式接收foo并使用

props: {foo: [type]
}

同时每当子组件中数据改变的时候,通过

this.$emit('update', newValue)

把参数newValue传递给父组件template中监听函数中的"val"。然后通过

val => bar = val

这个表达式就实现了bar = newValue. 这个时候,我们发现父组件中的关键数据bar被子组件改变(相等)了!

通过数据的双向绑定, 父(组件)可以修改子的数据, 子也可以修改父的数据

Vue提供了sync修饰符简化上面的代码,例如:

<comp :foo.sync="bar"></comp>

会被扩展为:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

然后你需要在子组件中改变父组件数据的时候, 需要触发以下的自定义事件:

this.$emit("update:foo", newValue)

【注意】你可能觉得这好像和我上面提到的二中的“通过自定义事件(emit)从子组件向父组件中传递数据”的那一节的内容似乎重叠了,。

然而并不是, 两者有着父子组件关系上的不同, 下面我通过一行关键的代码证明它们的区别所在

1.在我们讲解sync的这一小节里, 自定义事件发生时候运行的响应表达式是:
<son :foo="bar" v-on:update="val => bar = val"></son> 中的 "val => bar = val"
2.在二中的“通过自定义事件从子组件向父组件中传递数据” 里,自定义事件发生时候运行的响应表达式是:
<Son  v-on: eventYouDefined = "arg => functionYours(arg)" /> 中的 "arg => functionYours(arg)"

对前者, 表达式 val => bar = val意味着强制让父组件的数据等于子组件传递过来的数据, 这个时候,我们发现父子组件的地位是平等的。 父可以改变子(数据), 子也可以改变父(数据)

对后者, 你的functionYours是在父组件中定义的, 在这个函数里, 你可以对从子组件接受来的arg数据做任意的操作或处理决定权完全落在父组件中, 也就是:  父可以改变子(数据), 但子不能直接改变父(数据)!, 父中数据的变动只能由它自己决定

下面是一个展示demo:

父组件:

<template><div id="father"><div>我是父组件<son:wisdom.sync="wisdom":magic.sync="magic":attack.sync="attack":defense.sync="defense"></son><p>智力: {{ wisdom }}</p><p>膜法: {{ magic }}</p><p>攻击: {{ attack }}</p><p>防御: {{ defense }}</p></div></div>
</template><script>
import son from './son.vue'
export default {data: function () {return {wisdom: 90,magic: 160,attack: 100,defense: 80}},components: {son: son}
}</script><style scoped>
#father div {padding: 10px;margin: 10px;border: 1px solid grey;overflow: hidden;
}
</style>

子组件:

<template><div><p>我是子组件</p><p>智力: {{ wisdom }}</p><p>膜法: {{ magic }}</p><p>攻击: {{ attack }}</p><p>防御: {{ defense }}</p><button @click="increment('wisdom')">增加智力</button><button @click="increment('magic')">增加膜法</button><button @click="increment('attack')">增加攻击</button><button @click="increment('defense')">增加防御</button></div>
</template><script>
export default {props: {wisdom: Number,magic: Number,attack: Number,defense: Number},methods: {increment (dataName) {let newValue = this[dataName] + 1this.$emit(`update:${dataName}`, newValue)}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>button { float: left }
</style>

点击前:

点击增加子组件中“增加智力”按钮的时候, 父组件和子组件中的智力参数同时从90变为91


点击增加子组件中“增加膜法”按钮的时候, 父组件和子组件中的智力参数同时从160变为161

数据双向绑定是把双刃剑

从好处上看:
1.它实现了父子组件数据的“实时”同步, 在某些数据场景下可能会使用到这一点
2.sync提供的语法糖使得双向绑定的代码变得很简单

从坏处上看:
它破环了单向数据流的简洁性, 这增加了分析数据时的难度

当sync修饰的prop是个对象

我们对上面的例子修改一下, 把数据包裹在一个对象中传递下来:

父组件

<template><div id="father"><div>我是父组件<son :analysisData.sync="analysisData"></son><p>智力: {{ analysisData.wisdom }}</p><p>膜法: {{ analysisData.magic }}</p><p>攻击: {{ analysisData.attack }}</p><p>防御: {{ analysisData.defense }}</p></div></div>
</template><script>
import son from './son.vue'
export default {data: function () {return {analysisData: {wisdom: 90,magic: 160,attack: 100,defense: 80}}},components: {son: son}
}</script><style scoped>
#father div {padding: 10px;margin: 10px;border: 1px solid grey;overflow: hidden;
}
</style>

子组件:

<template><div><p>我是子组件</p><p>智力: {{ analysisData.wisdom }}</p><p>膜法: {{ analysisData.magic }}</p><p>攻击: {{ analysisData.attack }}</p><p>防御: {{ analysisData.defense }}</p><button @click="increment('wisdom')">增加智力</button><button @click="increment('magic')">增加膜法</button><button @click="increment('attack')">增加攻击</button><button @click="increment('defense')">增加防御</button></div>
</template><script>
export default {props: {analysisData: Object},methods: {increment (dataName) {let newObj = JSON.parse(JSON.stringify(this.analysisData))newObj[dataName] += 1this.$emit('update:analysisData', newObj)}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>button { float: left }
</style>

 demo同上

不要通过在子组件中修改引用类型props达到“父子组件数据同步”的需求!

父组件的数据传递给子组件, 一般通过props实现, 而在实现“父子组件数据同步”这一需求的时候, 小伙伴们可能会发现一点: 在子组件中修改引用类型的props(如数组和对象)是可行的

1.不仅可以达到同时修改父组件中的数据(因为本来引用的就是同一个数据)
2.而且还不会被Vue的检测机制发现!(不会报错)

但千万不要这样做, 这样会让数据流变得更加难以分析,如果你尝试这样做, 上面的做法可能会更好一些

不要这样做,糟糕的做法:

父组件:

<template><div id="father"><div>我是父组件<son :analysisData="analysisData"></son><p>智力: {{ analysisData.wisdom }}</p><p>膜法: {{ analysisData.magic }}</p><p>攻击: {{ analysisData.attack }}</p><p>防御: {{ analysisData.defense }}</p></div></div>
</template><script>
import son from './son.vue'
export default {data: function () {return {analysisData: {wisdom: 90,magic: 160,attack: 100,defense: 80}}},components: {son: son}
}</script><style scoped>
#father div {padding: 10px;margin: 10px;border: 1px solid grey;overflow: hidden;
}
</style>

子组件:

<template><div><p>我是子组件</p><p>智力: {{ analysisData.wisdom }}</p><p>膜法: {{ analysisData.magic }}</p><p>攻击: {{ analysisData.attack }}</p><p>防御: {{ analysisData.defense }}</p><button @click="increment ('wisdom')">增加智力</button><button @click="increment ('magic')">增加膜法</button><button @click="increment ('attack')">增加攻击</button><button @click="increment ('defense')">增加防御</button></div>
</template><script>
export default {props: {analysisData: Object},methods: {increment (dataName) {let obj = this.analysisDataobj[dataName] += 1}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>button { float: left }
</style>

demo同上, 但这并不是值得推荐的做法

【完】

【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据相关推荐

  1. 五分钟带你摸透 Vue组件及组件通讯

    一.组件化开发 组件 (Component) 是 Vue.js 强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代 码.在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能 ...

  2. 在已有vue项目中半途引入cube ui组件库的使用遇到的坑(血泪)

    直接进入正题(我这儿是属于cube-ui普通编译) 1,在你的vue项目中找到package.json文件安装cube-ui 终端输入命令   npm install cube-ui --save 2 ...

  3. vue 项目中使用 Loading 组件

    当在vue项目中请求后台接口时,常常会使用 loding 过渡数据的加载时间. 如果 loading 作为一个全局的加载状态,应该写在项目中的App.vue中 <template>< ...

  4. Vue | 实现页面跳转刷新,在Vue页面中调用其他页面的方法

    最近有一个登录界面的需求,感觉很简单,尝试各种方法每次都感觉快要成功了,但还是没能解决问题,果然没有系统学习就是很难立刻找到突破点,难以一语中的,好在历经千帆,在页面调用中就解决了这个问题. 需求描述 ...

  5. [转]Vue生态系统中的库

    Vue UI组件库 Vuex vux github ui demo:https://github.com/airyland/vux Mint UI 项目主页:http://mint-ui.github ...

  6. vue 项目中使用v-loading实现加载效果

    当在vue项目中请求后台接口时,常常会使用 loding 过渡数据的加载时间. 如果 loading 作为一个全局的加载状态,应该写在项目中的App.vue中 在el-table标签中添加v-load ...

  7. React组件进阶--组件通讯介绍,组件的 props特点,组件通讯的三种方式子到父,父到子,兄弟到兄弟组件,Context,回顾练习

    1.组件通讯介绍 组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据(state). 在组件化过程中,我们将一个完整的功能 拆分成多个组件,以更好的完成整个应用的功能. 而在这个过程中,多个组 ...

  8. Omi框架学习之旅 - 通过对象实例来实现组件通讯 及原理说明

    组件通讯不是讲完了吗(上帝模式还没讲哈),怎么又多了种方式啊. 你484傻,多一种选择不好吗? 其实这个不属于组件通讯啦,只是当父组件实例安装和渲染完毕后,可以执行installed这个方法(默认是空 ...

  9. VUE组件通讯——父子互传、互调

    目录 一.通过 $parent 和 $root 获取父/根组件实例 二.通过 $refs 获取子组件实例 在循环中使用ref 三.通过 prop 向子组件传值 命名方式 动态属性 字面量语法 vs 动 ...

最新文章

  1. idea如何设置自动换行
  2. 安卓最新系统_安卓最新10.0系统,新增功能都在这了!
  3. Golang练习题(自己认为比较不错的)
  4. 买断变订阅!苹果第一付费软件被骂上热搜 官方回应
  5. Android 设备正在“绞杀”密码!
  6. 支持向量机回归_机器学习系列17:支持向量机
  7. 基于块分割及CNN的文档矫正与光照消除方法 (有源码和数据)
  8. 修改现有用户帐户的 Microsoft Lync Server 2010 属性
  9. win10磁盘管理界面各系统分区介绍
  10. 2022-01-18国产Linux深度操作系统deepin20.4发布,涉及桌面和内核升级。
  11. 分布式机器学习\分布式KMeans
  12. Python函数的定义使用、return返回值、参数传递方式、结合字典列表循环的使用以及将函数存储在模块中
  13. Trivial、Non-Trivial和POD类型
  14. ptp输出内容包含什么_PTP技术及其应用分析
  15. 如何将页脚(footer)保持在页面底部
  16. 软考十大管理流程图知识点整理
  17. UML系列文章(6)---公共机制
  18. 2021 长安杯 Re
  19. 《沟通的技术——让交流、会议与演讲更有效》一1.1 一切尽在计划之中
  20. 第一集 斗罗世界 第八章

热门文章

  1. VC++ 中MSDataGrid控件的使用
  2. ASP.NET Razor (标记)语言概要
  3. SuperMap iClient for Leaflet入门学习
  4. cesium首次加载gltf模型成功
  5. 干货 | 国内互联网公司是如何做微服务实践的?(附PPT下载)
  6. 「ZJOI2016」大森林 解题报告
  7. 管理神话2:专家只有权这样做
  8. hdu 1150 Machine Schedule (经典二分匹配)
  9. ExtJS 等待两个/多个store加载完再执行操作
  10. hdu 1166 敌兵布阵