前言: 之前写过一篇文章《在不同场景下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来监听子组件触发的事件, 并且可以在监听函数中依次取得所有从子组件传来的参数

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

  1. this.emit('eventYouDefined', arg);

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

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

下面是一个实例

父组件

  1. <template>
  2. <div id="father">
  3. <div>
  4. 我是父组件,我接受到了:
  5. {{ text || '暂无数据' }}
  6. <son v-on:sendData='getSonText'></son>
  7. </div>
  8. </div>
  9. </template>
  10.  
  11. <script>
  12. import son from './son.vue'
  13. export default {
  14. data: function () {
  15. return {
  16. text: ''
  17. }
  18. },
  19. components: {
  20. son: son
  21. },
  22. methods: {
  23. getSonText (text) {
  24. this.text = text
  25. }
  26. }
  27. }
  28.  
  29. </script>
  30.  
  31. <style scoped>
  32. #father div {
  33. padding: 10px;
  34. margin: 10px;
  35. border: 1px solid grey;
  36. overflow: hidden;
  37. }
  38. </style>

子组件:

  1. <template>
  2. <div>
  3. <p>我是子组件,我所拥有的数据: {{ text }}</p>
  4. <button @click="sendData">
  5. 发送数据
  6. </button>
  7. </div>
  8. </template>
  9.  
  10. <script>
  11. export default {
  12. data () {
  13. return {
  14. text: '来自子组件的数据'
  15. }
  16. },
  17. methods: {
  18. sendData () {
  19. this.$emit('sendData', this.text)
  20. }
  21. }
  22. }
  23. </script>
  24.  
  25. <!-- Add "scoped" attribute to limit CSS to this component only -->
  26. <style scoped>
  27. button { float: left }
  28. </style>

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


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

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

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

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

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

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

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

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

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

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

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

父组件:

  1. <template>
  2. <div id="father">
  3. <div>
  4. 我是父组件,我接受到了:
  5. {{ text || '暂无数据' }}
  6. <button @click="getSonText()">接受数据</button>
  7. <son ref='son'></son>
  8. </div>
  9. </div>
  10. </template>
  11.  
  12. <script>
  13. import son from './son.vue'
  14. export default {
  15. data: function () {
  16. return {
  17. text: ''
  18. }
  19. },
  20. components: {
  21. son: son
  22. },
  23. methods: {
  24. getSonText () {
  25. this.text = this.$refs.son.text
  26. }
  27. }
  28. }
  29.  
  30. </script>
  31.  
  32. <style scoped>
  33. #father div {
  34. padding: 10px;
  35. margin: 10px;
  36. border: 1px solid grey;
  37. overflow: hidden;
  38. }
  39. </style>

子组件:

  1. <template>
  2. <div>
  3. <p>我是子组件,我所拥有的数据: {{ text }}</p>
  4. </div>
  5. </template>
  6.  
  7. <script>
  8. export default {
  9. data () {
  10. return {
  11. text: '来自子组件的数据'
  12. }
  13. }
  14. }
  15. </script>
  16.  
  17. <!-- Add "scoped" attribute to limit CSS to this component only -->
  18. <style scoped>
  19. button { float: left }
  20. </style>

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

点击接受数据按钮后:

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

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

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

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

这是父组件中的template:

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

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

  1. props: {
  2. foo: [type]
  3. }

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

  1. this.$emit('update', newValue)

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

  1. val => bar = val

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

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

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

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

会被扩展为:

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

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

  1. 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:

父组件:

  1. <template>
  2. <div id="father">
  3. <div>
  4. 我是父组件
  5. <son
  6. :wisdom.sync="wisdom"
  7. :magic.sync="magic"
  8. :attack.sync="attack"
  9. :defense.sync="defense">
  10. </son>
  11. <p>智力: {{ wisdom }}</p>
  12. <p>膜法: {{ magic }}</p>
  13. <p>攻击: {{ attack }}</p>
  14. <p>防御: {{ defense }}</p>
  15. </div>
  16. </div>
  17. </template>
  18.  
  19. <script>
  20. import son from './son.vue'
  21. export default {
  22. data: function () {
  23. return {
  24. wisdom: ,
  25. magic: ,
  26. attack: ,
  27. defense:
  28. }
  29. },
  30. components: {
  31. son: son
  32. }
  33. }
  34.  
  35. </script>
  36.  
  37. <style scoped>
  38. #father div {
  39. padding: 10px;
  40. margin: 10px;
  41. border: 1px solid grey;
  42. overflow: hidden;
  43. }
  44. </style>

子组件:

  1. <template>
  2. <div>
  3. <p>我是子组件</p>
  4. <p>智力: {{ wisdom }}</p>
  5. <p>膜法: {{ magic }}</p>
  6. <p>攻击: {{ attack }}</p>
  7. <p>防御: {{ defense }}</p>
  8. <button @click="increment('wisdom')">增加智力</button>
  9. <button @click="increment('magic')">增加膜法</button>
  10. <button @click="increment('attack')">增加攻击</button>
  11. <button @click="increment('defense')">增加防御</button>
  12. </div>
  13. </template>
  14.  
  15. <script>
  16. export default {
  17. props: {
  18. wisdom: Number,
  19. magic: Number,
  20. attack: Number,
  21. defense: Number
  22. },
  23.  
  24. methods: {
  25. increment (dataName) {
  26. let newValue = this[dataName] +
  27. this.$emit(`update:${dataName}`, newValue)
  28. }
  29. }
  30. }
  31. </script>
  32.  
  33. <!-- Add "scoped" attribute to limit CSS to this component only -->
  34. <style scoped>
  35. button { float: left }
  36. </style>

点击前:

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


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

数据双向绑定是把双刃剑

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

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

当sync修饰的prop是个对象

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

父组件

  1. <template>
  2. <div id="father">
  3. <div>
  4. 我是父组件
  5. <son :analysisData.sync="analysisData">
  6. </son>
  7. <p>智力: {{ analysisData.wisdom }}</p>
  8. <p>膜法: {{ analysisData.magic }}</p>
  9. <p>攻击: {{ analysisData.attack }}</p>
  10. <p>防御: {{ analysisData.defense }}</p>
  11. </div>
  12. </div>
  13. </template>
  14.  
  15. <script>
  16. import son from './son.vue'
  17. export default {
  18. data: function () {
  19. return {
  20. analysisData: {
  21. wisdom: ,
  22. magic: ,
  23. attack: ,
  24. defense:
  25. }
  26. }
  27. },
  28. components: {
  29. son: son
  30. }
  31. }
  32.  
  33. </script>
  34.  
  35. <style scoped>
  36. #father div {
  37. padding: 10px;
  38. margin: 10px;
  39. border: 1px solid grey;
  40. overflow: hidden;
  41. }
  42. </style>

子组件:

  1. <template>
  2. <div>
  3. <p>我是子组件</p>
  4. <p>智力: {{ analysisData.wisdom }}</p>
  5. <p>膜法: {{ analysisData.magic }}</p>
  6. <p>攻击: {{ analysisData.attack }}</p>
  7. <p>防御: {{ analysisData.defense }}</p>
  8. <button @click="increment('wisdom')">增加智力</button>
  9. <button @click="increment('magic')">增加膜法</button>
  10. <button @click="increment('attack')">增加攻击</button>
  11. <button @click="increment('defense')">增加防御</button>
  12. </div>
  13. </template>
  14.  
  15. <script>
  16. export default {
  17. props: {
  18. analysisData: Object
  19. },
  20.  
  21. methods: {
  22. increment (dataName) {
  23. let newObj = JSON.parse(JSON.stringify(this.analysisData))
  24. newObj[dataName] +=
  25. this.$emit('update:analysisData', newObj)
  26. }
  27. }
  28. }
  29. </script>
  30.  
  31. <!-- Add "scoped" attribute to limit CSS to this component only -->
  32. <style scoped>
  33. button { float: left }
  34. </style>

 demo同上

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

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

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

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

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

父组件:

  1. <template>
  2. <div id="father">
  3. <div>
  4. 我是父组件
  5. <son :analysisData="analysisData">
  6. </son>
  7. <p>智力: {{ analysisData.wisdom }}</p>
  8. <p>膜法: {{ analysisData.magic }}</p>
  9. <p>攻击: {{ analysisData.attack }}</p>
  10. <p>防御: {{ analysisData.defense }}</p>
  11. </div>
  12. </div>
  13. </template>
  14.  
  15. <script>
  16. import son from './son.vue'
  17. export default {
  18. data: function () {
  19. return {
  20. analysisData: {
  21. wisdom: ,
  22. magic: ,
  23. attack: ,
  24. defense:
  25. }
  26. }
  27. },
  28. components: {
  29. son: son
  30. }
  31. }
  32.  
  33. </script>
  34.  
  35. <style scoped>
  36. #father div {
  37. padding: 10px;
  38. margin: 10px;
  39. border: 1px solid grey;
  40. overflow: hidden;
  41. }
  42. </style>

子组件:

  1. <template>
  2. <div>
  3. <p>我是子组件</p>
  4. <p>智力: {{ analysisData.wisdom }}</p>
  5. <p>膜法: {{ analysisData.magic }}</p>
  6. <p>攻击: {{ analysisData.attack }}</p>
  7. <p>防御: {{ analysisData.defense }}</p>
  8. <button @click="increment ('wisdom')">增加智力</button>
  9. <button @click="increment ('magic')">增加膜法</button>
  10. <button @click="increment ('attack')">增加攻击</button>
  11. <button @click="increment ('defense')">增加防御</button>
  12. </div>
  13. </template>
  14.  
  15. <script>
  16. export default {
  17. props: {
  18. analysisData: Object
  19. },
  20.  
  21. methods: {
  22. increment (dataName) {
  23. let obj = this.analysisData
  24. obj[dataName] +=
  25. }
  26. }
  27. }
  28. </script>
  29.  
  30. <!-- Add "scoped" attribute to limit CSS to this component only -->
  31. <style scoped>
  32. button { float: left }
  33. </style>

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

【完】

【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据的更多相关文章

  1. Vue组件通讯

    Vue最常用的组件通讯有三种:父->子组件通讯.子->父组件通讯,兄弟组件通讯.(template用的pug模板语法) 1.父->子组件通讯 父->子组件通讯,是通过props ...

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

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

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

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

  4. openstack组件通讯端口定义

    openstack 组件通讯是通过ZeroMQ+ceilometer发送组件调用信息,具体是通过TCP通讯,发送数据和接收数据是用同一个端口(在配置文件指定),下面通过代码稍作解析: IceHouse ...

  5. Vue中父子组件通讯——组件todolist

    一.todolist功能开发 <div id="root"> <div> <input type="text" v-model=& ...

  6. vue中父子组件之间的传值、非父子组件之间的传值

    在Vue实例中每个组件之间都是相互独立的,都有自己的作用域,所以组件之间是不能直接获取数据.在项目开发中一个组件可能需要获取另一个组件的值,我们可以通过其他方法间接的获取.所以,获取的方法有以下几种: ...

  7. 深入理解Vue父子组件通讯的属性和事件

    在html中使用元素,会有一些属性,如class,id,还可以绑定事件,自定义组件也是可以的.当在一个组件中,使用了其他自定义组件时,就会利用子组件的属性和事件来和父组件进行数据交流. 父子组件之间的 ...

  8. vue项目中的父子组件之间的传值。

    首先说一下父子组件就是在一个vue文件中引入另一个vue文件,被引入vue文件就是子组件,引入vue文件的vue文件就是父组件.而在父组件中是不能直接调用子组件中的变量值的.下面详细说一下,父子组件之 ...

  9. 简述在Vue脚手架中,组件以及父子组件(非父子组件)之间的传值

    1.组件的定义 组成: template:包裹HTML模板片段(反映了数据与最终呈现给用户视图之间的映射关系) 只支持单个template标签: 支持lang配置多种模板语法: script:配置Vu ...

随机推荐

  1. LINUX下的远端主机登入 校园网络注册 网络数据包转发和捕获

    第一部分:LINUX 下的远端主机登入和校园网注册 校园网内目的主机远程管理登入程序 本程序为校园网内远程登入,管理功能,该程序分服务器端和客户端两部分:服务器端为remote_server_udp. ...

  2. (转)每天一个linux命令(28):tar命令

    场景:Linux环境中压缩和解压的命令经常会使用到,所以学好这个命令很有必要! 原文出处:http://www.cnblogs.com/peida/archive/2012/11/30/2795656 ...

  3. Java中的clone()----深复制,浅复制

    这篇文章主要介绍了Java中对象的深复制(深克隆)和浅复制(浅克隆) ,需要的朋友可以参考下 1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他 ...

  4. AC自动机总结及板子(不带指针)

    蒟蒻最近想学个AC自动机简直被网上的板子搞疯了,随便点开一个都是带指针的,然而平时用到指针的时候并不多,看到这些代码也完全是看不懂的状态.只好在大概理解后自己脑补(yy)了一下AC自动机的代码,居然还 ...

  5. ORA-12516: TNS: 监听程序找不到符合协议堆栈要求的可用处理程”的异常

    简单说明:我们开发时多人开发,会频繁访问服务器数据库,结果当连接数大的时候,就会报ora-12516的错误,ORA-12516: TNS: 监听程序找不到符合协议堆栈要求的可用处理程"的异常 ...

  6. ES6 变量、常量声明总结

    较之前ES5,新颁布在声明上有改变 一.var  对比  let 1.作用域不同 let只在命令所在的代码块 {} 里有效 ES5只有全局作用域和函数作用域,没有块级作用域,带来很多不合理的场景,比如 ...

  7. 纯JS实现像素逐渐显示

    就是对于新手的我,以前从来没有做过对像素进行操作的实例.于是把资料书找了出来,实现了这个功能,比较简单,大神勿喷.下面是效果图,因为重在思路,效果就简陋一些. 其实就是简单的用JS实现将左上角的矩形随 ...

  8. Abp(.NetCore)开发与发布过程3-部署Ubuntu站点

    以下是笔者在 Ubuntu 16.0-64bit 环境下 发布 ABP(.NetCore)的全过程.特此记录,希望对大家有所帮助. 准备的工具 1.PuTTY(ssh,如果不想每次都用阿里云的远程登录 ...

  9. Head First 设计模式 第4章工厂模式

    第4章 工厂模式 在介绍工厂模式之前,先让我们来看一个例子. 这里有一个Pizza类,用来生产pizza,并返回对象,具体代码如下: package com.ek.factory.simple; im ...

  10. Prerequisite check "CheckActiveFilesAndExecutables" failed.

    错误日志: [Aug , :: AM] Prerequisite check "CheckActiveFilesAndExecutables" failed. The detail ...