前言: 之前写过一篇文章《在不同场景下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: ,
magic: ,
attack: ,
defense:
}
},
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] +
this.$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: ,
magic: ,
attack: ,
defense:
}
}
},
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] +=
this.$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: ,
magic: ,
attack: ,
defense:
}
}
},
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.analysisData
obj[dataName] +=
}
}
}
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</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. ASP.NET MVC 开发微信支付H5(外置浏览器支付)

    H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付. 主要用于触屏版的手机浏览器请求微信支付的场景.可以方便的从外部浏览器 ...

  2. 基于Hexo搭建博客并部署到Github Pages

    基于Hexo搭建博客并部署到Github Pages 之前在简书上写东西,觉得自己还是太浮躁.本来打算用Flask自己写一个,以为是微框架就比较简单,naive.HTML.CSS.JS等都要学啊,我几 ...

  3. python教程6-3:排序

    (a).输入一串数字.并从大到小排列. (b).跟a一样,不过要用字典序从大到小排列. python35 PaiXu_6_3.py PaiXu_6_3.py #coding=utf-8 import ...

  4. 44. leetcode 28. Implement strStr()

    28. Implement strStr() Implement strStr(). Returns the index of the first occurrence of needle in ha ...

  5. 实例甜点 Unreal Engine 4迷你教程(5)之函数中的静态变量

    本小节的教程无前置教程,可直接学习,篇幅很短. 本教程浓缩起来就是一句话:函数中的静态变量在调试过程中保留值.所以需要谨慎对待. 什么意思?请先不要一步一步对着做,而整体地看一遍下面的过程: 第一步: ...

  6. matplotlib实现数据可视化

    一篇matplotlib库的学习博文.matplotlib对于数据可视化非常重要,它完全封装了MatLab的所有API,在python的环境下和Python的语法一起使用更是相得益彰. 一.库的安装和 ...

  7. spring Bean类自动装载实现

    先贴spring的开发文档,有助于大家学习http://shouce.jb51.net/spring/beans.html#beans-factory-class 一直想研究一下spring bean ...

  8. Python爬虫从入门到放弃(二十三)之 Scrapy的中间件Downloader Middleware实现User-Agent随机切换

    总架构理解Middleware 通过scrapy官网最新的架构图来理解: 这个图较之前的图顺序更加清晰,从图中我们可以看出,在spiders和ENGINE提及ENGINE和DOWNLOADER之间都可 ...

  9. promise处理多个相互依赖的异步请求

    在项目中,经常会遇到多个相互依赖的异步请求.如有a,b,c三个ajax请求,b需要依赖a返回的数据,c又需要a和b请求返回的数据.如果采用请求嵌套请求的方式自然是不可取的.导致代码难以维护,如何请求很 ...

  10. Java动态代理学习【Spring AOP基础之一】

    Spring AOP使用的其中一个底层技术就是Java的动态代理技术.Java的动态代理技术主要围绕两个类进行的 java.lang.reflect.InvocationHandler java.la ...