vue组件通信方式全面详解

众所周知,Vue主要思想就是组件化开发。因为,在实际的项目开发中,肯定会以组件的开发模式进行。形如页面和页面之间需要通信一样,Vue 组件和组件之间肯定也需要互通有无、共享状态。接下来,我们就悉数给大家展示所有 Vue 组件之间的通信方式。

组件关系

  • App组件和A组件、A组件和B组件、B组件和C组件形成父子关系
  • B组件和D组件形成兄弟关系
  • App组件和C组件、App和B组件形成了隔代关系(其中的层级可能是多级,既隔多代)

组件通信


这么多的组件关系,那么组件和组件之间又有哪些通信的方式呢?各种方式的区别又是什么?适用场景又是什么呢?

props和$emit

这种方式是我们日常开发中应用最多的一种方式。

props以单向数据流的形式可以很好的完成父子组件的通信

所谓单向数据流:就是数据只能通过 props 由父组件流向子组件,而子组件并不能通过修改 props 传过来的数据修改父组件的相应状态。至于为什么这样做,Vue 官网做出了解释:

**所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

正因为这个特性,于是就有了对应的 $emit。$emit 用来触发当前实例上的事件。对此,我们可以在父组件自定义一个处理接受变化状态的逻辑,然后在子组件中如若相关的状态改变时,就触发父组件的逻辑处理事件。

let Child = {
template: `<div>
<input type="text" v-model='msg'/>
<button @click='handleClick'>传递</button>
</div>`,
props: ['msg'],
methods: {
handleClick() {
this.$emit('getChildData', '子组件数据')
}
}, }
let Parent = {
data() {
return {
msg: '小马哥',
val:''
}
},
methods: {
getChildData(val) {
this.val = val;
}
},
template: `
<div>
<p>我是一个父组件</p>
<p>我是{{val}}</p>
<Child :msg='msg' @getChildData='getChildData'></Child>
</div>
`,
components: {
Child
}
} let vm = new Vue({
el: '#app',
template: `
<div>
<Parent></Parent>
</div>
`,
components: {
Parent
} })
  1. 父传子:父组件传递msg数据给子组件,通过v-bind绑定msg,子组件中直接可以用props接收绑定的数据
  2. 子传父:子组件触发相应的事件,通过$emit触发事件传递数据,父组件中绑定对应的事件,通过$on监听对应的事件 接收子组件传递的数据

EventBus-中央事件总线

如果想实现兄弟组件之间进行通信,在项目规模不大的情况下,完全可以使用中央事件总线EventBus的方式。如果你的项目规模是大中型的,那我们会使用vuex状态管理

EventBus通过新建一个Vue事件bus对象,通过bus.$emit触发事件,bus.$on监听触发的事件。

Vue.component('A', {
template: `
<div>
<p>我是A组件</p>
<button @click='handleClick'>A传递到B</button>
</div>
`,
data() {
return {
msg: 'hello 小马哥'
}
},
methods: {
handleClick() {
this.$bus.$emit('globalEvent',this.msg);
}
},
})
Vue.component('B', {
template: `
<div>
<p>我是B组件</p>
<h3>{{aValue}}</h3>
</div>
`,
data() {
return {
aValue: ''
}
},
created () {
this.$bus.$on('globalEvent',(val)=>{
this.aValue = val;
})
},
})
// 定义中央事件总线
let bus = new Vue();
// 将中央事件总线赋值给Vue.prototype中,这样所有组件都能访问到了
Vue.prototype.$bus = bus; let vm = new Vue({
el: '#app',
template: `
<div>
<A></A>
<B></B>
</div>
`, })

$attrs和$listeners

通过 props进行组件通信的方式只适合直接的父子组件,如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A直接想传递数据给组件C那就行不通了! 只能是组件A通过 props 将数据传给组件B,然后组件B获取到组件A 传递过来的数据后再通过 props 将数据传给组件C。当然这种方式是非常复杂的,无关组件中的逻辑业务一种增多了,代码维护也没变得困难,再加上如果嵌套的层级越多逻辑也复杂,无关代码越多!

针对这样一个问题,Vue 2.4提供了$attrs 和$listeners来实现能够直接让组件A传递消息给组件C

Vue.component('A', {
template: `
<div>
<p>我是A组件</p>
<B :msg='msg' @getCData='getCData'></B>
</div>
`,
methods: {
getCData(val) {
alert(val)
}
},
data() {
return {
msg: 'hello 小马哥'
}
},
})
Vue.component('B', {
template: `
<div>
<p>我是B组件</p>
<!-- C组件中能直接触发 getCData 的原因在于:B组件调用 C组件时,使用 v-on 绑定了 $listeners 属性 -->
<!-- 通过v-bind 绑定 $attrs 属性,C组件可以直接获取到 A组件中传递下来的 props(除了 B组件中 props声明的) -->
<C v-bind='$attrs' v-on='$listeners'></C>
</div>
`,
// props: ['msg'],
data() {
return { }
}
})
Vue.component('C', {
template: `
<div>
<p>我是C组件</p>
<p>{{$attrs.msg}}</p>
<button @click='handleClick'>传递数据</button>
</div>
`,
methods: {
handleClick() {
this.$emit('getCData', 'C组件的数据')
}
},
data() {
return { }
}
})
let vm = new Vue({
el: '#app',
template: `
<div>
<A></A>
</div>
`, })
  • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定属性 (class和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。
  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

provide和inject

在父组件中通过 provider 来提供属性,然后在子组件中通过 inject 来注入变量。不论子组件有多深,只要调用了 inject 那么就可以注入在 provider 中提供的数据,而不是局限于只能从当前父组件的 prop 属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。这和 React 中的 Context API 有没有很相似!

Vue.component('A', {
template: `
<div>
<p>我是A组件</p>
<B></B>
</div>
`,
provide:{
a:"祖先A的数据"
},
data() {
return {
msg: 'hello 小马哥'
}
},
})
Vue.component('B', {
template: `
<div>
<p>我是B组件</p>
<C></C>
</div>
`, data() {
return { }
}
})
Vue.component('C', {
template: `
<div>
<p>我是C组件</p>
<h3>{{a}}</h3>
</div>
`,
inject:['a'],
data() {
return { }
}
})
let vm = new Vue({
el: '#app',
template: `
<div>
<A></A>
</div>
`, })
  • 在 parent 组件中,通过 provide 属性,以对象的形式向子孙组件暴露了一些属性
  • 在 child 组件中,通过 inject 属性注入了 parent 组件提供的数据,实际这些通过 inject 注入的属性是挂载到 Vue 实例上的,所以在组件内部可以通过 this 来访问

⚠️ 注意:官网文档提及 provide 和 inject 主要为高阶插件/组件库提供用例,并不推荐直接用于应用程序代码中。

$parent和$children

这里要说的这种方式就比较直观了,直接操作父子组件的实例。$parent 就是父组件的实例对象,而 $children 就是当前实例的直接子组件实例了,不过这个属性值是数组类型的,且并不保证顺序,也不是响应式的。

Vue.component('Parent', {
template: `
<div>
<p>我是父组件</p>
{{msg}}
<hr/>
<Child></Child>
</div>
`,
mounted () {
//读取子组件数据,注意$children并不保证顺序,也不是响应式的
console.log(this.$children[0].a)
},
data() {
return {
msg: 'hello 小马哥'
}
},
})
Vue.component('Child', {
template: `
<div>
<p>我是孩子组件</p>
<input type="text" @input='changeValue'/>
<h2>{{myMsg}}</h2>
</div>
`,
methods: {
changeValue() {
this.$parent.msg = '子组件中的数据'
}
},
data() {
return {
myMsg:this.$parent.msg,
a:"小马哥"
}
}
}) let vm = new Vue({
el: '#app',
template: `
<div>
<Parent></Parent>
</div>
`,
})

Vuex状态管理

Vuex 是状态管理工具,实现了项目状态的集中式管理。工具的实现借鉴了 Flux、Redux、和 The Elm Architecture 的模式和概念。当然与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。详细的关于 Vuex 的介绍,你既可以去查看官网文档,也可以查看本专栏关于 Vuex 一系列的介绍。

Vue组件通信方式全面详解的更多相关文章

  1. vue组件生命周期详解

    Vue所有的生命周期钩子自动绑定在this上下文到实例中,因此你可以访问数据,对属性和方法进行运算.这意味着你不能使用箭头函数来定义一个生命周期方法.这是因为箭头函数绑定了父上下文,因此this与你期 ...

  2. vue组件is属性详解

    查看官网对is属性的讲解,请移步:vue.js 本文参考资料 在vue.js组件教程的一开始提及到了is特性 下面是官网对is属性使用的说明: 组件功能是vue项目的一大特色.组件可以扩展html元素 ...

  3. vue组件间传值详解

    1.父传子----传值要点: <1> 在组件注册的时候必须要使用 return 去返回 data对象;

  4. vc中调用Com组件的方法详解

    vc中调用Com组件的方法详解 转载自:网络,来源未知,如有知晓者请告知我.需求:1.创建myCom.dll,该COM只有一个组件,两个接口:   IGetRes--方法Hello(),   IGet ...

  5. 整理4种Vue组件通信方式

    整理4种Vue组件通信方式 重点是梳理了前两个,父子组件通信和eventBus通信,我觉得Vue文档里的说明还是有一些简易,我自己第一遍是没看明白. 父子组件的通信 非父子组件的eventBus通信 ...

  6. iOS 组件化流程详解(git创建流程)

    [链接]组件化流程详解(一)https://www.jianshu.com/p/2deca619ff7e

  7. Vue.js 数据绑定语法详解

    Vue.js 数据绑定语法详解 一.总结 一句话总结:Vue.js 的模板是基于 DOM 实现的.这意味着所有的 Vue.js 模板都是可解析的有效的 HTML,且通过一些特殊的特性做了增强.Vue ...

  8. React—组件生命周期详解

    React—组件生命周期详解 转自 明明的博客  http://blog.csdn.net/slandove/article/details/50748473 (非原创) 版权声明:转载请注明出处,欢 ...

  9. Vue基础语法-数据绑定、事件处理和扩展组件等知识详解(案例分析,简单易懂,附源码)

    前言: 本篇文章主要讲解了Vue实例对象的创建.常用内置指令的使用.自定义组件的创建.生命周期(钩子函数)等.以及个人的心得体会,汇集成本篇文章,作为自己对Vue基础知识入门级的总结与笔记. 其中介绍 ...

随机推荐

  1. Tornado 简述

    前言 python 旗下,群英荟萃,豪杰并起.单是用于 web 开发的,就有 webpy.web2py.bottle.pyramid.zope2.flask.tornado.django 等等,不一而 ...

  2. flask-restful 初探

    flask-restful 是 Flask 的一个用于支持 RESTful 的插件. 刚开始用对我来说还是比较坑的... 目录结构 / /test /test/common /__init__.py ...

  3. 想要成为一名优秀的Java程序员,你需要这8个锦囊

    私底下,隔三差五就有读者问我:"二哥,怎么样才能像你一样,成为一名优秀的 Java 开发者呢?"假如把"怎么才能像你一样"去掉的话,这个问题就是一个好问题,否则 ...

  4. Mac使用brew安装MongoDB

    之前一直使用以下命令安装MongoDB,但是一直安装不上 brew install mongodb 后来看了官网,安装方法如下 brew tap mongodb/brew //这步不知道需不需要 br ...

  5. Vue项目一、node.js和npm的安装和环境搭建

    一.为什么安装node.js及npm npm npm是Node.js的包管理工具(package manager),是全球最大的生态系统,同过npm可以找到很多丰富的插件来满足项目的需求. a1.现在 ...

  6. Anroid关于fragment控件设置长按事件无法弹出Popupwindows控件问题解决记录

    一.问题描述     记录一下最近在安卓的gragment控件中设置长按事件遇见的一个坑!!!     在正常的activity中整个活动中设置长按事件我通常实例化根部局,例如LinearLayout ...

  7. Java基础面试系列(一)

    Java基础面试总结(一) 1. 面向对象和面向过程的区别 面向过程 面向对象 性能 高于面向对象 类加载的时候需要实例化,比较消耗资源 三易(易维护,易复用,易扩展) 不如面向对象 具有封装,继承, ...

  8. 通过nodejs实现文件的上传

    通过nodejs实现文件的上传 主要内容 本文将用来讲述如何通过nodejs进行文件上传,将会涉及到以下知识点: 通过express模块进行服务器的搭建 通过multer模块将上传的文件保存到指定目录 ...

  9. 爬取疫情数据,以django+pyecharts实现数据可视化web网页

    在家呆着也是呆着,不如做点什么消磨时间呗~ 试试用django+pyecharts实现疫情数据可视化web页面 这里要爬疫情数据 来自丁香园.搜狗及百度的疫情实时动态展示页 先看看劳动成果: 导航栏: ...

  10. vue的$message(提示框换行)

    之前一直在搜怎么让提示框的文字换行,网上搜到的基本都是使用 ‘ /n ’,使用无效,也试了css换行,本来想用弹窗自己编辑html内容,还好回去官网看了一下: let arr = ['测试一', '测 ...