• 售前

  • 售后

热门帖子
入门百科

Vue中的父子组件通讯以及利用sync同步父子组件数据

[复制链接]
穿雨捶 显示全部楼层 发表于 2021-10-26 13:41:36 |阅读模式 打印 上一主题 下一主题
目次


  • 前言
  • 子组件向父组件中转达数据

    • 一. 通过props从父向子组件转达函数,调用函数改变父组件数据
    • 二. 通过自界说变乱从子组件向父组件中转达数据
    • 三. 通过ref属性在父组件中直接取得子组件的数据(data)

  • 通过sync实现数据双向绑定, 从而同步父子组件数据
  • 数据双向绑定是把双刃剑
  • 当sync修饰的prop是个对象
  • 不要通过在子组件中修改引用类型props达到“父子组件数据同步”的需求!

前言

父子组件通讯,可分为两种环境:
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 (在兄弟组件的数据互换那一节)

二. 通过自界说变乱从子组件向父组件中转达数据

我们可以在子组件中通过$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. <script>
  11. import son from './son.vue'
  12. export default {
  13.   data: function () {
  14.     return {
  15.       text: ''
  16.     }
  17.   },
  18.   components: {
  19.     son: son
  20.   },
  21.   methods: {
  22.     getSonText (text) {
  23.       this.text = text
  24.     }
  25.   }
  26. }
  27. </script>
  28. <style scoped>
  29. #father div {
  30.   padding: 10px;
  31.   margin: 10px;
  32.   border: 1px solid grey;
  33.   overflow: hidden;
  34. }
  35. </style>
复制代码
子组件:
  1. <template>
  2.   <div>
  3.     <p>我是子组件,我所拥有的数据: {{ text }}</p>
  4.     <button @click="sendData">
  5.       发送数据
  6.     </button>
  7.   </div>
  8. </template>
  9. <script>
  10. export default {
  11.   data () {
  12.     return {
  13.       text: '来自子组件的数据'
  14.     }
  15.   },
  16.   methods: {
  17.     sendData () {
  18.       this.$emit('sendData', this.text)
  19.     }
  20.   }
  21. }
  22. </script>
  23. <!-- Add "scoped" attribute to limit CSS to this component only -->
  24. <style scoped>
  25.    button { float: left }
  26. </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. <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 () {
  24.       this.text = this.$refs.son.text
  25.     }
  26.   }
  27. }
  28. </script>
  29. <style scoped>
  30. #father div {
  31.   padding: 10px;
  32.   margin: 10px;
  33.   border: 1px solid grey;
  34.   overflow: hidden;
  35. }
  36. </style>
复制代码
子组件:
  1. <template>
  2.   <div>
  3.     <p>我是子组件,我所拥有的数据: {{ text }}</p>
  4.   </div>
  5. </template>
  6. <script>
  7. export default {
  8.   data () {
  9.     return {
  10.       text: '来自子组件的数据'
  11.     }
  12.   }
  13. }
  14. </script>
  15. <!-- Add "scoped" attribute to limit CSS to this component only -->
  16. <style scoped>
  17.    button { float: left }
  18. </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. <script>
  19. import son from './son.vue'
  20. export default {
  21.   data: function () {
  22.     return {
  23.       wisdom: 90,
  24.       magic: 160,
  25.       attack: 100,
  26.       defense: 80
  27.     }
  28.   },
  29.   components: {
  30.     son: son
  31.   }
  32. }
  33. </script>
  34. <style scoped>
  35. #father div {
  36.   padding: 10px;
  37.   margin: 10px;
  38.   border: 1px solid grey;
  39.   overflow: hidden;
  40. }
  41. </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. <script>
  15. export default {
  16.   props: {
  17.     wisdom: Number,
  18.     magic: Number,
  19.     attack: Number,
  20.     defense: Number
  21.   },
  22.   methods: {
  23.     increment (dataName) {
  24.       let newValue = this[dataName] + 1
  25.       this.$emit(`update:${dataName}`, newValue)
  26.     }
  27.   }
  28. }
  29. </script>
  30. <!-- Add "scoped" attribute to limit CSS to this component only -->
  31. <style scoped>
  32.    button { float: left }
  33. </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. <script>
  15. import son from './son.vue'
  16. export default {
  17.   data: function () {
  18.     return {
  19.       analysisData: {
  20.         wisdom: 90,
  21.         magic: 160,
  22.         attack: 100,
  23.         defense: 80
  24.       }
  25.     }
  26.   },
  27.   components: {
  28.     son: son
  29.   }
  30. }
  31. </script>
  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>我是子组件</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. <script>
  15. export default {
  16.   props: {
  17.     analysisData: Object
  18.   },
  19.   methods: {
  20.     increment (dataName) {
  21.       let newObj = JSON.parse(JSON.stringify(this.analysisData))
  22.       newObj[dataName] += 1
  23.       this.$emit('update:analysisData', newObj)
  24.     }
  25.   }
  26. }
  27. </script>
  28. <!-- Add "scoped" attribute to limit CSS to this component only -->
  29. <style scoped>
  30.    button { float: left }
  31. </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. <script>
  15. import son from './son.vue'
  16. export default {
  17.   data: function () {
  18.     return {
  19.       analysisData: {
  20.         wisdom: 90,
  21.         magic: 160,
  22.         attack: 100,
  23.         defense: 80
  24.       }
  25.     }
  26.   },
  27.   components: {
  28.     son: son
  29.   }
  30. }
  31. </script>
  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>我是子组件</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. <script>
  15. export default {
  16.   props: {
  17.     analysisData: Object
  18.   },
  19.   methods: {
  20.     increment (dataName) {
  21.       let obj = this.analysisData
  22.       obj[dataName] += 1
  23.     }
  24.   }
  25. }
  26. </script>
  27. <!-- Add "scoped" attribute to limit CSS to this component only -->
  28. <style scoped>
  29.    button { float: left }
  30. </style>
复制代码
以上就是Vue中的父子组件通讯以及利用sync同步父子组件数据的详细内容,更多关于Vue的资料请关注草根技术分享别的相干文章!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作