Vue通过provide inject实现组件通信


Posted in Javascript onSeptember 03, 2020

provide/inject是Vue.js2.2.0版本后新增的API:

provide:Object | () => Object//一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。

inject:Array<string> | { [key: string]: string | Symbol | Object }//一个字符串数组,或一个对象

虽然官方文档说,provide和inject主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中,但是在插件 / 组件库(比如 iView,事实上 iView 的很多组件都在用)。不过建议归建议,如果你用好了,这个 API 会非常有用。

这对选项需要一起使用,以允许一个祖先组件向其所有的子孙后代注入一个依赖,不论组件的层次有多深,并在起上下游关系成立的时间里始终生效。

注意:provide和inject绑定并不是可响应的。这显然不是设计的失误,而是刻意的。

下面我们来看一看它最简单的用法:

//祖先级组件(上级组件)
<template>
  <div>
    <Pro></Pro>
  </div>
</template>
<script>
import Pro from '../components/provide.vue';
export default {
  data(){
    return{
    }
  },
   provide:{
    foo:'test'
  },
  components:{
    Pro,
  }
}
</script>
<style scoped>
</style>
//子孙级组件(下级组件)
<template>
  <div>
    <p>{{foo}}</p>
  </div>
</template>
<script>
export default {
  data(){
    return {
    }
  },
  inject:['foo'],
}
</script>
<style scoped>
</style>

我们在上级组件中设置了一个provide:foo,值为test,它的作用就是将foo这个变量提供给它的所有下级组件。而在下级组件中通过inject注入了从上级组件中提供的foo变量,那么在下级组件中,就可以直接通过this.foo来访问了。

再次强调一遍,provide和inject绑定并不是可响应的,所以上述例子中上级组件的foo改变了,下级组件的this.foo的值还是不会改变的。

我们一般会在main.js中导入app.vue作为根组件,我们需要在app.vue上做文章,这就是我们实现功能的关键。我们可以这样理解:app.vue作为一个最外层的根组件,用来存储所有需要的全局数据和状态。因为项目中的所有组件(包含路由),它的父组件(或根组件)都是app.vue,所有我们可以把整个app.vue实例通过provide对外提供。那么,所有的组件都能共享其数据,方法等。

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  provide () {
   return {
    app: this
   }
  }
 }
</script>

上面,我们把整个app.vue的实例`this`对外提供,接下来,任何组件(或路由)只要通过`inject`注入app.vue的话,都可以通过this.app.xxx的形式来访问app.vue的data,computed,method等内容。

app.vue是整个项目第一个被渲染的组件,而且只会渲染一次(即使切换路由,app.vue也不会被再次渲染),利用这个特性,很适合做一次性全局的状态数据管理,例如我们将用户的登录信息保存起来:

//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>

这样,任何页面或组件只要通过inject注入app后,就可以直接访问userInfo的数据了,比如:

<template>
 <div>
  {{ app.userInfo }}
 </div>
</template>
<script>
export default {
  inject: ['app']
 }
</script>

是不是很简单呢。除了直接使用数据,还可以调用方法。比如在某个页面里,修改了个人资料,这时一开始在app.vue里获取的userInfo已经不是最新的了,需要重新获取。可以这样使用:

//某个页面:
 
<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>

同样非常简单。只要理解了 `this.app` 是直接获取整个 `app.vue` 的实例后,使用起来就得心应手了。想一想,配置复杂的 Vuex 的全部功能,现在是不是都可以通过 `provide / inject` 来实现了呢?

如果你顾忌 Vue.js 文档中所说,provide / inject 不推荐直接在应用程序中使用,那没有关系,仍然使用你熟悉的 Vuex 或 Bus 来管理你的项目就好。我们介绍的这对 API,主要还是在独立组件中发挥作用的。

只要一个组件使用了 `provide` 向下提供数据,那其下所有的子组件都可以通过 `inject` 来注入,不管中间隔了多少代,而且可以注入多个来自不同父级提供的数据。需要注意的是,一旦注入了某个数据,比如上面示例中的 `app`,那这个组件中就不能再声明 `app` 这个数据了,因为它已经被父级占有。

进阶技巧:

如果你的项目足够复杂,或需要多人协同开发时,在app.vue里会写非常多的代码,多到结构复杂难以维护。这时可以使用 Vue.js 的混合mixins,将不同的逻辑分开到不同的 js 文件里。

我先简单介绍一下什么是mixins:

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。(个人理解mixins就是定义一部分公共的方法或者计算属性,然后混入到各个组件中使用,方便管理与统一修改)

比如上面的用户信息,就可以放到混合里:

//新建文件(user.js)
export default {
 data () {
  return {
   userInfo: null
  }
 },
 methods: {
  getUserInfo () {
   // 这里通过 ajax 获取用户信息后,赋值给 this.userInfo,以下为伪代码
   $.ajax('/user/info', (data) => {
    this.userInfo = data;
   });
  }
 },
 mounted () {
  this.getUserInfo();
 }
}

然后在app.vue中混合:

<script>
 import mixins_user from'../mixins/user.js';
 export default {
  mixins: [mixins_user],
  data () {
   return {
   }
  }
 }
</script>

这样,跟用户信息相关的逻辑,都可以在user.js里维护,或者由某个人来维护,app.vue也就很容易维护了。

要深入了解混入请参照官方文档:https://cn.vuejs.org/v2/guide/mixins.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery UI Autocomplete 1.8.16 中文输入修正代码
Apr 16 Javascript
JS判断客户端是手机还是PC的2个代码
Apr 12 Javascript
js实现网页自动刷新可制作节日倒计时效果
May 27 Javascript
JavaScript实现列出数组中最长的连续数
Dec 29 Javascript
javascript实现的猜数小游戏完整实例代码
May 10 Javascript
javascript设置文本框光标的方法实例小结
Nov 04 Javascript
JS定时器用法分析【时钟与菜单中的应用】
Dec 21 Javascript
利用n工具轻松管理Node.js的版本
Apr 21 Javascript
json的结构与遍历方法实例分析
Apr 25 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
Jun 28 Javascript
JavaScript类的继承方法小结【组合继承分析】
Jul 11 Javascript
vue-cli脚手架的安装教程图解
Sep 02 Javascript
Vue组件通信$attrs、$listeners实现原理解析
Sep 03 #Javascript
Vue父组件监听子组件生命周期
Sep 03 #Javascript
JavaScript 几种循环方式以及模块化的总结
Sep 03 #Javascript
Vuejs通过拖动改变元素宽度实现自适应
Sep 02 #Javascript
Vue Object.defineProperty及ProxyVue实现双向数据绑定
Sep 02 #Javascript
Vue 组件的挂载与父子组件的传值实例
Sep 02 #Javascript
vue中的.$mount('#app')手动挂载操作
Sep 02 #Javascript
You might like
火车头discuz6.1 完美采集的php接口文件
2009/09/13 PHP
判断是否为指定长度内字符串的php函数
2010/02/16 PHP
Javascript与PHP验证用户输入URL地址是否正确
2014/10/09 PHP
PHP技术开发微信公众平台
2015/07/22 PHP
php封装的数据库函数与用法示例【参考thinkPHP】
2016/11/08 PHP
PHP中时间加减函数strtotime用法分析
2017/04/26 PHP
JavaScript CSS修改学习第二章 样式
2010/02/19 Javascript
jQuery实现随意改变div任意属性的名称和值(部分原生js实现)
2013/05/28 Javascript
js 单击式的下拉菜单效果实例
2013/08/13 Javascript
jQuery中ajax和post处理json的不同示例对比
2014/11/02 Javascript
如何实现移动端浏览器不显示 pc 端的广告
2015/10/15 Javascript
JS实现点击按钮获取页面高度的方法
2015/11/02 Javascript
angular.js实现列表orderby排序的方法
2018/10/02 Javascript
JS与SQL方式随机生成高强度密码示例
2018/12/29 Javascript
vue简单封装axios插件和接口的统一管理操作示例
2020/02/02 Javascript
深入解析Python中函数的参数与作用域
2016/03/20 Python
python3设计模式之简单工厂模式
2017/10/17 Python
python实现定时自动备份文件到其他主机的实例代码
2018/02/23 Python
python3+PyQt5实现柱状图
2018/04/24 Python
Python通过调用有道翻译api实现翻译功能示例
2018/07/19 Python
Python基于分析Ajax请求实现抓取今日头条街拍图集功能示例
2018/07/19 Python
python3 拼接字符串的7种方法
2018/09/12 Python
Python os.rename() 重命名目录和文件的示例
2018/10/25 Python
Python创建一个空的dataframe,并循环赋值的方法
2018/11/08 Python
Numpy对数组的操作:创建、变形(升降维等)、计算、取值、复制、分割、合并
2019/08/28 Python
Django多层嵌套ManyToMany字段ORM操作详解
2020/05/19 Python
python进度条显示-tqmd模块的实现示例
2020/08/23 Python
Python操作dict时避免出现KeyError的几种解决方法
2020/09/20 Python
世界上最大的在线旅行社新加坡网站:Expedia新加坡
2016/08/25 全球购物
法国滑雪假期的专家:Ski Planet
2019/11/02 全球购物
Python里面search()和match()的区别
2016/09/21 面试题
毕业生文员求职信
2013/11/03 职场文书
《一件运动衫》教学反思
2014/02/19 职场文书
股东授权委托书范文
2014/09/13 职场文书
2014年幼儿园学期工作总结
2014/12/05 职场文书
2014年妇女工作总结
2014/12/06 职场文书