vue组件间通信六种方式(总结篇)


Posted in Javascript onMay 15, 2019

前言

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:

vue组件间通信六种方式(总结篇)

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

针对不同的使用场景,如何选择行之有效的通信方式?这是我们所要探讨的主题。本文总结了vue组件间通信的几种方式,如props、 $emit / $on 、vuex、 $parent / $children 、 $attrs / $listeners 和provide/inject,以通俗易懂的实例讲述这其中的差别及使用场景,希望对小伙伴有些许帮助。

本文的代码请猛戳github博客 ,纸上得来终觉浅,大家动手多敲敲代码!

方法一、 props / $emit

父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

1.父组件向子组件传值

接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件Users.vue中如何获取父组件App.vue中的数据  users:["Henry","Bucky","Emily"]

//App.vue父组件 
<template> 
 <div id="app"> 
 <users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名 
 </div> 
</template> 
<script> 
import Users from "./components/Users" 
export default { 
 name: 'App', 
 data(){ 
 return{ 
 users:["Henry","Bucky","Emily"] 
 } 
 }, 
 components:{ 
 "users":Users 
 } 
} 
//users子组件 
<template> 
 <div class="hello"> 
 <ul> 
 <li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面 
 </ul> 
 </div> 
</template> 
<script> 
export default { 
 name: 'HelloWorld', 
 props:{ 
 users:{ //这个就是父组件中子标签自定义名字 
 type:Array, 
 required:true 
 } 
 } 
} 
</script>

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

2.子组件向父组件传值(通过事件形式)

接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。

vue组件间通信六种方式(总结篇)

// 子组件 
<template> 
 <header> 
 <h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件 
 </header> 
</template> 
<script> 
export default { 
 name: 'app-header', 
 data() { 
 return { 
 title:"Vue.js Demo" 
 } 
 }, 
 methods:{ 
 changeTitle() { 
 this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值” 
 } 
 } 
} 
</script> 
// 父组件 
<template> 
 <div id="app"> 
 <app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致 
 // updateTitle($event)接受传递过来的文字 
 <h2>{{title}}</h2> 
 </div> 
</template> 
<script> 
import Header from "./components/Header" 
export default { 
 name: 'App', 
 data(){ 
 return{ 
 title:"传递的是一个值" 
 } 
 }, 
 methods:{ 
 updateTitle(e){ //声明这个函数 
 this.title = e; 
 } 
 }, 
 components:{ 
 "app-header":Header, 
 } 
} 
</script>

总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二、 $emit / $on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex 。

1.具体实现方式:  

var Event=new Vue(); 
Event.$emit(事件名,数据); 
Event.$on(事件名,data => {});

2.举个例子:兄弟组件有三个,分别是A、B、C,C组件如何获取A或者B组件的数据

 

<div id="itany"> 
 <my-a></my-a> 
 <my-b></my-b> 
 <my-c></my-c> 
</div> 
<template id="a"> 
 <div> 
 <h3>A组件:{{name}}</h3> 
 <button @click="send">将数据发送给C组件</button> 
 </div> 
</template> 
<template id="b"> 
 <div> 
 <h3>B组件:{{age}}</h3> 
 <button @click="send">将数组发送给C组件</button> 
 </div> 
</template> 
<template id="c"> 
 <div> 
 <h3>C组件:{{name}},{{age}}</h3> 
 </div> 
</template> 
<script> 
var Event = new Vue();//定义一个空的Vue实例 
var A = { 
 template: '#a', 
 data() { 
 return { 
 name: 'tom' 
 } 
 }, 
 methods: { 
 send() { 
 Event.$emit('data-a', this.name); 
 } 
 } 
} 
var B = { 
 template: '#b', 
 data() { 
 return { 
 age: 20 
 } 
 }, 
 methods: { 
 send() { 
 Event.$emit('data-b', this.age); 
 } 
 } 
} 
var C = { 
 template: '#c', 
 data() { 
 return { 
 name: '', 
 age: "" 
 } 
 }, 
 mounted() {//在模板编译完成后执行 
 Event.$on('data-a',name => { 
 this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event 
 }) 
 Event.$on('data-b',age => { 
 this.age = age; 
 }) 
 } 
} 
var vm = new Vue({ 
 el: '#itany', 
 components: { 
 'my-a': A, 
 'my-b': B, 
 'my-c': C 
 } 
}); 
</script>

vue组件间通信六种方式(总结篇)

方法三、vuex

vue组件间通信六种方式(总结篇)

1.简要介绍Vuex原理

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

2.简要介绍各模块在流程中的功能:

Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。

dispatch:操作行为触发方法,是唯一能执行action的方法。

actions: 操作行为处理模块,由组件中的 $store.dispatch('action 名称', data1) 来触发。然后由commit()来触发mutation的调用 , 间接更新 state 。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。

commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。

mutations: 状态改变操作方法,由actions中的 commit('mutation 名称') 来触发 。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。

state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。

getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

3.Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态, 具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

let defaultCity = "上海" 
try { // 用户关闭了本地存储功能,此时在外层加个try...catch 
 if (!defaultCity){ 
 defaultCity = JSON.parse(window.localStorage.getItem('defaultCity')) 
 } 
}catch(e){} 
export default new Vuex.Store({ 
 state: { 
 city: defaultCity 
 }, 
 mutations: { 
 changeCity(state, city) { 
 state.city = city 
 try { 
 window.localStorage.setItem('defaultCity', JSON.stringify(state.city)); 
 // 数据改变的时候把数据拷贝一份保存到localStorage里面 
 } catch (e) {} 
 } 
 } 
})

这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:

JSON.stringify(state.subscribeList); // array -> string 
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array

方法四、 $attrs / $listeners

1.简介

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法, 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用 。

// demo.vue 
 <template> 
 <div> 
 <child-com:foo="foo":boo="boo":coo="coo":doo="doo"></child-com> 
 </div> 
 </tempalte> 
 <script> 
 const childCom = ()=> import('./childCom1.vue') 
 export default { 
 data () { 
 return { 
 foo: 'Hello World!', 
 boo: 'Hello Javascript!', 
 coo: 'Hello Vue', 
 doo: 'Last' 
 } 
 }, 
 components: { childCom } 
 } 
 </script> 
// childCom1.vue 
<template> 
 <div> 
 <p>foo: {{ foo }}</p> 
 <p>attrs: {{ $attrs }}</p> 
 <child-com2 v-bind="$attrs"></child-com2> 
 </div> 
</template> 
<script> 
const childCom2 = ()=> import('./childCom2.vue') 
export default { 
 props: ['foo'], // foo作为props属性绑定 
 inheritAttrs: false, 
 created () { 
 console.log(this.$attrs) // { boo: 'Hello Javascript!', coo: 'Hello Vue', doo: 'Last' } 
 } 
} 
</script> 
// childCom2.vue 
<template> 
 <div> 
 <p>boo: {{ boo }}</p> 
 <p>attrs: {{ $attrs }}</p> 
 <child-com3 v-bind="$attrs"></child-com3> 
 </div> 
</template> 
<script> 
const childCom3 = ()=> import('./childCom3.vue') 
export default { 
 props: ['boo'] // boo作为props属性绑定 
 inheritAttrs: false, 
 created () { 
 console.log(this.$attrs) // { coo: 'Hello Vue', doo: 'Last' } 
 } 
} 
</script>

$attrs 表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了 $attrs  ,  $listeners  来传递数据与事件,跨级组件之间的通讯变得更简单

方法五、provide/inject

1.简介

Vue2.2.0新增API,这对选项需要一起使用, 以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效 。一言而蔽之: 祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。

2.举个例子

假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件

// A.vue 
export default { 
 provide: { 
 name: '浪里行舟' 
 } 
} 
// B.vue 
export default { 
 inject: ['name'], 
 mounted () { 
 console.log(this.name); // 浪里行舟 
 } 
}

可以看到,在 A.vue 里,我们设置了一个  provide: name ,值为 浪里行舟,它的作用就是将  name  这个变量提供给它的所有子组件。而在 B.vue 中,通过  inject  注入了从 A 组件中提供的  name  变量,那么在组件 B 中,就可以直接通过  this.name  访问这个变量了,它的值也是 浪里行舟。这就是 provide / inject API 最核心的用法。

需要注意的是: provide 和 inject 绑定并不是可响应的 。不过,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。   所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。

方法六、 $parent  /  $children 与  ref

ref :如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

$parent  /  $children :访问父 / 子实例

需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用  ref 来访问组件的例子:

// component-a 子组件 
export default { 
 data () { 
 return { 
 title: 'Vue.js' 
 } 
 }, 
 methods: { 
 sayHello () { 
 window.alert('Hello'); 
 } 
 } 
} 
// 父组件 
<template> 
 <component-a ref="comA"></component-a> 
</template> 
<script> 
 export default { 
 mounted () { 
 const comA = this.$refs.comA; 
 console.log(comA.title); // Vue.js 
 comA.sayHello(); // 弹窗 
 } 
 } 
</script>

不过, 这两种方法的弊端是,无法在跨级或兄弟间通信 。

// parent.vue 
<component-a></component-a> 
<component-b></component-b> 
<component-b></component-b>

我们想在 component-a 中,访问到引用它的页面中(这里就是 parent.vue)的两个 component-b 组件,那这种情况下,就得配置额外的插件或工具了,比如 Vuex 和 Bus 的解决方案。

总结

常见使用场景可以分为三类:

  • 父子通信:   父向子传递数据是通过 props,子向父是通过 events( $emit );通过父链 / 子链也可以通信( $parent  /  $children );ref 也可以访问组件实例;provide / inject API。
  • 兄弟通信:   Bus;Vuex;
  • 跨级通信:   Bus;Vuex;provide / inject API、 $attrs/$listeners

以上所述是小编给大家介绍的vue组件间通信六种方式(总结篇),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
JS实现至少包含字母、大小写数字、字符的密码等级的两种方法
Feb 03 Javascript
以Python代码实例展示kNN算法的实际运用
Oct 26 Javascript
妙用Bootstrap的 popover插件实现校验表单提示功能
Aug 29 Javascript
学好js,这些js函数概念一定要知道【推荐】
Jan 19 Javascript
Node做中转服务器转发接口
Oct 18 Javascript
微信小程序 如何引入外部字体库iconfont的图标
Jan 31 Javascript
React 使用browserHistory项目访问404问题解决
Jun 01 Javascript
详解Angular6学习笔记之主从组件
Sep 05 Javascript
手把手教你写一个微信小程序(推荐)
Oct 17 Javascript
微信小程序学习笔记之登录API与获取用户信息操作图文详解
Mar 29 Javascript
js图片查看器插件用法示例
Jun 22 Javascript
JavaScript 接口原理与用法实例详解
May 12 Javascript
JS正则表达式封装与使用操作示例
May 15 #Javascript
微信小程序实现授权登录
May 15 #Javascript
基于vue实现一个神奇的动态按钮效果
May 15 #Javascript
微信小程序导航栏跟随滑动效果的实现代码
May 14 #Javascript
详解VSCode配置启动Vue项目
May 14 #Javascript
微信小程序下拉菜单效果的实例代码
May 14 #Javascript
jquery+css实现Tab栏切换的代码实例
May 14 #jQuery
You might like
Thinkphp3.2.3整合phpqrcode生成带logo的二维码
2016/07/21 PHP
PHP多进程之pcntl_fork的实例详解
2017/10/15 PHP
PHP获取类私有属性的3种方法
2020/09/10 PHP
不能再简单的无闪刷新验证码原理很简单
2007/11/05 Javascript
DHTML Slide Show script图片轮换
2008/03/03 Javascript
javascript 表单验证常见正则
2009/09/28 Javascript
解决3.01版的jquery.form.js中文乱码问题的解决方法
2012/03/08 Javascript
js arguments对象应用介绍
2012/11/28 Javascript
jQuery实现模拟marquee标签效果
2015/07/14 Javascript
JQuery异步加载PartialView的方法
2016/06/07 Javascript
Bootstrap精简教程中秋大放送
2016/09/15 Javascript
JavaScript中const、var和let区别浅析
2016/10/11 Javascript
bootstrap多种样式进度条展示
2016/12/20 Javascript
微信小程序 特效菜单抽屉效果实例代码
2017/01/11 Javascript
xmlplus组件设计系列之路由(ViewStack)(7)
2017/05/02 Javascript
浅谈Vuex@2.3.0 中的 state 支持函数申明
2017/11/22 Javascript
浏览器调试动态js脚本的方法(图解)
2018/01/19 Javascript
Angular利用trackBy提升性能的方法
2018/01/26 Javascript
vue项目搭建以及全家桶的使用详细教程(小结)
2018/12/19 Javascript
js数组相减简单示例【删除a数组所有与b数组相同元素】
2020/03/04 Javascript
如何实现js拖拽效果及原理解析
2020/05/08 Javascript
原生JS实现pc端轮播图效果
2020/12/21 Javascript
[01:02:48]2018DOTA2亚洲邀请赛小组赛 A组加赛 Newbee vs Liquid
2018/04/03 DOTA
Python异常处理总结
2014/08/15 Python
Python中的下划线详解
2015/06/24 Python
如何准确判断请求是搜索引擎爬虫(蜘蛛)发出的请求
2015/10/13 Python
python+matplotlib绘制饼图散点图实例代码
2018/01/20 Python
python opencv设置摄像头分辨率以及各个参数的方法
2018/04/02 Python
Python玩转加密的技巧【推荐】
2019/05/13 Python
python 怎样将dataframe中的字符串日期转化为日期的方法
2019/09/26 Python
Numpy中ndim、shape、dtype、astype的用法详解
2020/06/14 Python
大学生毕业的自我鉴定
2013/11/13 职场文书
自荐信写法介绍
2014/01/25 职场文书
教研活动总结
2014/04/28 职场文书
2015年度保密工作总结
2015/04/24 职场文书
详解Node.js如何处理ES6模块
2021/05/15 Javascript