[Toc] >[success] # 组件通信 ~~~ 1.常见的组件通信方式使用'props'父传子,使用'$emit' 子传父,使 用'bus'/'vuex' 实现兄弟组件通信,使用一些特别的方式例如'ref','$parent' ,'$children'。 2.另外一种不常见的,组件的通信 'provide / inject',这种通信和'vuex','bus' 不同,它是一种vue.js 内置的接口 3. 'provide / inject' 和'props' 不同点是'props' 只能进行父子之间的传值, 简单的说就是一层,但如果想爷孙传值就不行,爷孙直接的可以使用 'provide / inject' 可以理解是一个加强版本的'props',但做不到兄弟组件通信 ,兄弟间还是需要使用'bus' 和'vuex' ,但是也有其他办法利用 'provide / inject'实现 4.provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了 一个可监听的对象,那么其对象的属性还是可响应的 ~~~ >[info] ## 使用provide / inject <a href='https://cn.vuejs.org/v2/api/#provide-inject'>官网讲解</a> ~~~ 1.解释单词意思方便更好的理解 ,provide -- 提供 , inject -- 注入 2.官方文档不推荐使用,但是iview作者给出的建议是可以好好利用让其变得强大。 3.'provide:Object | () => Object' 'inject:Array<string> | { [key: string]: string | Symbol | Object }' 官方给的两个参数接收的值,其中provide 支持对象和一个方法返回对象 ,下面的案例将对这个详细讲解 ~~~ >[danger] ##### provide 对象形式使用 * 下面代码实现效果: ![](https://box.kancloud.cn/9d8110772b38e1d36b9fe09362c903ba_213x32.png) ~~~ 1.使用'provide' 对象的形式,注意如果使用对象的形式,是无法获得当前 ,组件的'this'指向因此也无法获取其中的data 2.下面案例中 'Acomponents' 组件中嵌套了'Bcomponents',在一个视图组 件中去调用'Acomponents' 这样这个视图和'Bcomponents' 形成了爷孙关系 ~~~ * Acomponents.vue 组件中 ~~~ <template> <div> <Bcomponents></Bcomponents> </div> </template> <script> import Bcomponents from '@/components/b-components' export default { components:{ Bcomponents }, } </script> <style scoped> </style> ~~~ * 组件 Bcomponents 写法 ~~~ <template> <div> 我接受爷爷组件的传值{{name}} </div> </template> <script> export default { inject: ['name'], } </script> <style scoped> </style> ~~~ * 调用组件Acomponents 的视图组件写法 ~~~ <template> <div class="home"> <Acomponents></Acomponents> </div> </template> <script> // @ is an alias to /src import Acomponents from '@/components/a-components' export default { name: 'home', // 这里是对象 provide:{ name:"1111" // name:this.dataName 会报错不支持这么使用 }, data(){ return{ dataName:"我给孙子的值" } }, components: { Acomponents, } } </script> ~~~ >[danger] ##### provide 使用方法返回对象 * 代码效果图 ![](https://box.kancloud.cn/6dedfc7c1f4d8b2d43f8ef52c9f14581_293x40.png) ~~~ 1. 'provide' 在刚才案例中使用对象的形式传入this,会报错但是不能传递 固定值,为了让代码更灵活,使用方法返回对象的形式如下代码。 ~~~ * Acomponents.vue 组件中 ~~~ <template> <div> <Bcomponents></Bcomponents> </div> </template> <script> import Bcomponents from '@/components/b-components' export default { components:{ Bcomponents }, } </script> <style scoped> </style> ~~~ * 组件 Bcomponents 写法 ~~~ <template> <div> 我接受爷爷组件的传值{{name}} </div> </template> <script> export default { inject: ['name'], } </script> <style scoped> </style> ~~~ * 调用组件Acomponents 的视图组件写法 ~~~ <template> <div class="home"> <Acomponents></Acomponents> </div> </template> <script> // @ is an alias to /src import Acomponents from '@/components/a-components' export default { name: 'home', // 这里是方法 provide(){ return{ name:this.dataName } }, data(){ return{ dataName:"我给孙子的值" } }, components: { Acomponents, } } </script> ~~~ >[danger] ##### provide技巧 ~~~ 1.在文章开头就说了'provide 和 inject 绑定并不是可响应的。这是刻意为之 的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应 的'简单的说父组件提供给子组件的内容,当父组件再次改变数据的时候, 子组件是收不到的,并且子组件也无法更改父组件传递的值 2.但是官方的这句话也说了,如果是一个可监听的对象就可以,这样就有一 个解决方法,就是父组件在提供属性值的时候提供的参数是对象,或者是 当前的this本身 3.在vue2.6版本提供了一个'Vue.observable' 来帮忙解决如果传递一个this过 于浪费,和如果传递必须要把数据变成对象的形式的尴尬 ~~~ * 父组件 ~~~ <template> <div class="home"> <input v-model="dataName.name"> <Acomponents></Acomponents> </div> </template> <script> // @ is an alias to /src import Acomponents from '@/components/a-components' import Vue from "vue"; export default { name: 'home', provide(){ this.dataName = Vue.observable({ name: "blue" }); return{ dataName:this.dataName } }, components: { Acomponents, } } </script> ~~~ * 子组件 ~~~ <template> <div> <input v-model="dataName.name"> 我接受爷爷组件的传值{{dataName.name}} </div> </template> <script> export default { inject: ['dataName'], } </script> <style scoped> </style> ~~~ >[info] ## 根据案例衍生 ~~~ 1.思考什么时候可以利用这个属性?如何利用这个属性可以达到最佳 2.答:当我们封装一个select 标签的时候,我们将select 和里面的opition, 拆成两个组件去写和维护,这时候'opition' 组件想获取'select' 组件的值时候 ,可以利用这个或者'props' 来传递 3.答:如何达到最佳,当我们使用'provide' 时候使用方法返回对象可以传递 一个this,如果我们直接将祖先组件的'this' 传递过去,就会发现在子孙组件 中就可以去调用祖先组件的中的内容 ~~~ >[danger] ##### 关于问题一种select 组件 <a href="https://www.jianshu.com/p/6dc5ea5ca95e">直接参考简书文章</a> >[danger] ##### 利用问题二的特性做一个Vuex ~~~ 1.下面的案例不是为了替代Vuex,只是一种方案来实现Vuex 2.根据cli搭建的项目,我们可以发现'app.vue'实际上是所有的根,而且 app.vue 是整个项目第一个被渲染的组件,而且只会渲染一次(即使切换路 由,app.vue 也不会被再次渲染),利用这个特性,很适合做一次性全局的 状态数据管理 3.尝试写一个全局登陆用户信息案例 ~~~ * 首先下面代码是对第二条的解释(常见的app.vue一般只有一个router-view) ~~~ <template> <div> <router-view></router-view> </div> </template> <script> export default { } </script> ~~~ * 开始案例利用provide 方法的形式返回app.vue this执行,让全局组件获取 到登陆信息 ~~~ <template> <div> <router-view></router-view> </div> </template> <script> export default { provide () { return { app: this } } } </script> ~~~ * 接下来,任何组件(或路由)只要通过 inject 注入 app.vue 的 app 的话,都可以直接通过 this.app.xxx 来访问 app.vue 的 data、computed、methods (下面代码还是写在app.vue文件中的) ~~~ <script> export default { provide () { return { app: this } }, data () { return { userInfo: null } }, methods: { getUserInfo () { // 这里通过 ajax 获取用户信息后,赋值给 this.userInfo,以下为伪代码 $.ajax('/user/info', (data) => { this.userInfo = data; }); } }, mounted () { this.getUserInfo(); } } </script> ~~~ * 在组件中或者其他视图组件中获取(userInfo 信息) ~~~ <template> <div> {{ app.userInfo }} </div> </template> <script> export default { inject: ['app'] } </script> ~~~ * 如果某个组件中改变从app中的值,需要全局统一的时候可以这么做,更改对应app.vue中的值 ~~~ <template> <div> {{ app.userInfo }} </div> </template> <script> export default { inject: ['app'], methods: { changeUserInfo () { // 这里修改完用户数据后,通知 app.vue 更新,以下为伪代码 $.ajax('/user/update', () => { // 直接通过 this.app 就可以调用 app.vue 里的方法 this.app.getUserInfo(); }) } } } </script> ~~~