Vue 2.0 中依赖注入 provide/inject组合实战


Posted in Javascript onJune 20, 2019

用法

--------------------------------------------------------------------------------

先来看看官网的介绍:

Vue 2.0 中依赖注入 provide/inject组合实战

简单的说,当组件的引入层次过多,我们的子孙组件想要获取祖先组件得资源,那么怎么办呢,总不能一直取父级往上吧,而且这样代码结构容易混乱。这个就是这对选项要干的事情

provide和inject需要配合使用,它们的含义如下:

provide        ;一个对象或返回一个对象的函数,该对象包含可注入起子孙的属性,可以使用ES6的Symbols作为key(只有原生支持Symbol才可以)
inject         ;一个字符串数组或一个对象
            

;字符串数组    ;provide对象里哪些属性可用

            ;一个对象        ;key是本地的绑定名,value是provide里对应的对象名,也可以是一个对象,此时from属性是provide里对应的对象名,default属性是不存在时的默认值

来个实例就明显了:

<!DOCTYPE html>  <!--例1-->


<html lang="en"> 
<head>
  <meta charset="UTF-8">
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <title>Document</title>  
</head>
<body>
  <div id="app"><child></child></div>
  <script>
    Vue.component('child',{
      inject:['message'],
      template:'<p>{{message}}</p>'
    })
    new Vue({
      el:'#app',provide:{message:'Hello Vue!'}
    })
  </script>
</body>
</html>

输出:Hello Vue!,对应的DOM节点渲染为:

Vue 2.0 中依赖注入 provide/inject组合实战

是不是感觉和props的传值差不多,我们在中间再嵌套一层组件就知道他的用处了,例如:

<!DOCTYPE html> <!--例2-->


<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
  <div id="app"><test></test></div>
  <script>
    Vue.component('child',{
      inject:['message'],
      template:'<p>{{message}}</p>'
    })
    Vue.component('test',{
      template:`<div><child></child></div>`
    })
    new Vue({
      el:'#app',provide:{message:'Hello Vue!'}
    })
  </script>
</body>
</html>

输出:Hello Vue!,对应的DOM节点渲染为:

 Vue 2.0 中依赖注入 provide/inject组合实战

就是这个用处吧,多层嵌套时还是很方便的

源码分析

--------------------------------------------------------------------------------

  provide/inject组合的源码分为三个部分,分别是组件注册、Vue实例化和组件实例化的过程,如下:

组件注册时

注册时会执行Vue.extend()(第4770行),内部会执行mergeOptions()合并一些属性,mergeOptions如下:

function mergeOptions (  //第1451行
 parent,
 child,
 vm
) {
 {
  checkComponents(child);
 }

 if (typeof child === 'function') {
  child = child.options;
 }

 normalizeProps(child, vm);
 normalizeInject(child, vm);     //对inject进行一次规范化
 normalizeDirectives(child);
 var extendsFrom = child.extends;
 if (extendsFrom) {
  parent = mergeOptions(parent, extendsFrom, vm);
 }
 if (child.mixins) {
  for (var i = 0, l = child.mixins.length; i < l; i++) {
   parent = mergeOptions(parent, child.mixins[i], vm);
  }
 }
 var options = {};
 var key;
 for (key in parent) {
  mergeField(key);
 }
 for (key in child) {
  if (!hasOwn(parent, key)) {
   mergeField(key);
  }
 }
 function mergeField (key) {
  var strat = strats[key] || defaultStrat;
  options[key] = strat(parent[key], child[key], vm, key);
 }
 return options
}

normalizeInject定义如下:

function normalizeInject (options, vm) { //第1398行
 var inject = options.inject;
 if (!inject) { return }
 var normalized = options.inject = {};
 if (Array.isArray(inject)) {           //如果inject是一个数组
  for (var i = 0; i < inject.length; i++) {      //遍历inject
   normalized[inject[i]] = { from: inject[i] };    //保存到normalized里面,例如:{foo: {from: "foo"}}
  }
 } else if (isPlainObject(inject)) {        //如果inject是一个对象
  for (var key in inject) {
   var val = inject[key];
   normalized[key] = isPlainObject(val)
    ? extend({ from: key }, val)
    : { from: val };
  }
 } else {
  warn(
   "Invalid value for option \"inject\": expected an Array or an Object, " +
   "but got " + (toRawType(inject)) + ".",
   vm
  );
 }
}

对于例1来说,mergeOptions()之后inject等于:{message: {from: "message"}},如下:

Vue 2.0 中依赖注入 provide/inject组合实战

Vue实例化时

执行_init()时会执行mergeOptions()进行数据的合并,对于provide的合并策略等于mergeDataOrFn()函数(和data的合并策略是一样的,定义在1321行),返回一个匿名函数(第1154行),如下:

function mergeDataOrFn (  //第1154行
 parentVal,
 childVal,
 vm
) {
 if (!vm) {          //这是组件的分支
  // in a Vue.extend merge, both should be functions
  if (!childVal) {
   return parentVal
  }
  if (!parentVal) {
   return childVal
  }
  // when parentVal & childVal are both present,
  // we need to return a function that returns the
  // merged result of both functions... no need to
  // check if parentVal is a function here because
  // it has to be a function to pass previous merges.
  return function mergedDataFn () {
   return mergeData(
    typeof childVal === 'function' ? childVal.call(this, this) : childVal,
    typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal
   )
  }
 } else {          //这是非组件的实例,返回一个函数
  return function mergedInstanceDataFn () {
   // instance merge
   var instanceData = typeof childVal === 'function'
    ? childVal.call(vm, vm)
    : childVal;
   var defaultData = typeof parentVal === 'function'
    ? parentVal.call(vm, vm)
    : parentVal;
   if (instanceData) {
    return mergeData(instanceData, defaultData)
   } else {
    return defaultData
   }
  }
 }
}

然后返回到_init()之后会调用initProvide()初始化provide:

function initProvide (vm) {   //第3619行
 var provide = vm.$options.provide;        //尝试获取provide
 if (provide) {                  //如果provide存在,当它是函数时执行该返回,否则直接将provide保存到Vue实例的_provided属性上
  vm._provided = typeof provide === 'function' 
   ? provide.call(vm)
   : provide;
 }
}

返回后_provided等于{message:"Hello Vue!"},如下

e]Vue 2.0 中依赖注入 provide/inject组合实战

组件实例化时

 _init()时会执行initInjections(),经过了前面两步的处理,这里比较简单了,直接从父Vue或父Vue的父Vue获取对应的值即可,如下:

function initInjections (vm) {   //第2681行 初始化inject
 var result = resolveInject(vm.$options.inject, vm);     //遍历祖先节点,获取对应的inject,例如:比如:{foo: "bar"}
 if (result) {                        //如果获取了对应的值,则将它变成响应式
  toggleObserving(false);
  Object.keys(result).forEach(function (key) {
   /* istanbul ignore else */
   {
    defineReactive(vm, key, result[key], function () { //将key编程响应式,这样就可以访问该元素了
     warn(
      "Avoid mutating an injected value directly since the changes will be " +
      "overwritten whenever the provided component re-renders. " +
      "injection being mutated: \"" + key + "\"",
      vm
     );
    });
   }
  });
  toggleObserving(true);
 }
}

function resolveInject (inject, vm) {      //第3649行 确定Inject inject:例如:{foo: {from: "foo"}} vm:当前组件的实例
 if (inject) {                          //如果inject非空
  // inject is :any because flow is not smart enough to figure out cached
  var result = Object.create(null);               //存储最后的结果
  var keys = hasSymbol
   ? Reflect.ownKeys(inject).filter(function (key) {          //如果有符号类型,调用Reflect.ownKeys()返回所有的key,再调用filter
    /* istanbul ignore next */
    return Object.getOwnPropertyDescriptor(inject, key).enumerable
   })
   : Object.keys(inject);                       //获取所有的key,此时keys就是个字符串数组,比如:["foo"]

  for (var i = 0; i < keys.length; i++) {                //这里遍历每个key
   var key = keys[i];
   var provideKey = inject[key].from;
   var source = vm;
   while (source) {
    if (source._provided && hasOwn(source._provided, provideKey)) {  //如果source存在_provided 且 含有provideKey这个属性
     result[key] = source._provided[provideKey];             //则将值保存到result[key]中
     break                                //并跳出while循环
    }
    source = source.$parent;                     //否则将source赋值给父Vue实例,直到找到对应的providekey为止
   }
   if (!source) {                           //如果最后source不存在,即没有从当前实例或祖先实例的_provide找到privideKey这个key
    if ('default' in inject[key]) {
     var provideDefault = inject[key].default;             //如果有定义defult,则使用默认值
     result[key] = typeof provideDefault === 'function'
      ? provideDefault.call(vm)
      : provideDefault;
    } else {
     warn(("Injection \"" + key + "\" not found"), vm);
    }
   }
  }
  return result                            //返回结果,比如:{foo: "bar"}
 }
}

注:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

总结

以上所述是小编给大家介绍的Vue 2.0 中依赖注入 provide/inject组合实战,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
javascrpt绑定事件之匿名函数无法解除绑定问题
Dec 06 Javascript
js取得url地址参数实例
Feb 22 Javascript
JavaScript中如何通过arguments对象实现对象的重载
May 12 Javascript
node.js中的fs.readSync方法使用说明
Dec 17 Javascript
js仿黑客帝国字母掉落效果代码分享
Nov 08 Javascript
javascript实现状态栏中文字动态显示的方法
Oct 20 Javascript
javascript字符串函数汇总
Dec 06 Javascript
浅谈jQuery操作类数组的工具方法
Dec 23 Javascript
js-FCC算法-No repeats please字符串的全排列(详解)
May 02 Javascript
微信小程序画布圆形进度条显示效果
Nov 17 Javascript
常见的浏览器存储方式(cookie、localStorage、sessionStorage)
May 07 Javascript
微信小程序实现蓝牙打印
Sep 23 Javascript
js实现页面多个日期时间倒计时效果
Jun 20 #Javascript
vue实现绑定事件的方法实例代码详解
Jun 20 #Javascript
javascript实现5秒倒计时并跳转功能
Jun 20 #Javascript
JS实现的简单tab切换功能完整示例
Jun 20 #Javascript
Vue 页面权限控制和登陆验证功能的实例代码
Jun 20 #Javascript
jQuery实现文本显示一段时间后隐藏的方法分析
Jun 20 #jQuery
javascript获取select值的方法完整实例
Jun 20 #Javascript
You might like
php zip文件解压类代码
2009/12/02 PHP
关于PHPDocument 代码注释规范的总结
2013/06/25 PHP
解析file_get_contents模仿浏览器头(user_agent)获取数据
2013/06/27 PHP
php header函数的常用http头设置
2015/06/25 PHP
将CKfinder整合进CKEditor3.0的新方法
2010/01/10 Javascript
json2.js的初步学习与了解
2011/10/06 Javascript
基于jquery的放大镜效果
2012/05/30 Javascript
jquery封装的对话框简单实现
2013/07/21 Javascript
js 验证身份证信息有效性
2014/03/28 Javascript
JavaScript如何禁止Backspace键
2015/12/02 Javascript
JavaScript暂停和继续定时器的实现方法
2016/07/18 Javascript
JS封装通过className获取元素的函数示例
2016/12/20 Javascript
Angular2开发——组件规划篇
2017/03/28 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
2018/01/29 jQuery
关于vue中的ajax请求和axios包问题
2018/04/19 Javascript
Vue 组件传值几种常用方法【总结】
2018/05/28 Javascript
javascript设计模式 ? 策略模式原理与用法实例分析
2020/04/21 Javascript
JS遍历树层级关系实现原理解析
2020/08/31 Javascript
[56:00]DOTA2上海特级锦标赛主赛事日 - 4 胜者组决赛Secret VS Liquid第一局
2016/03/05 DOTA
[00:52]DOTA2齐天大圣预告片
2016/08/13 DOTA
Python实现破解12306图片验证码的方法分析
2017/12/29 Python
python3射线法判断点是否在多边形内
2019/06/28 Python
Django DRF认证组件流程实现原理详解
2020/08/17 Python
欧洲最大的婴幼儿服装及内衣公司:Petit Bateau(小帆船)
2016/08/16 全球购物
June Jacobs尊积帕官网:知名的spa水疗护肤品牌
2019/03/21 全球购物
自荐信要包含哪些内容
2013/11/06 职场文书
大学生自助营养快餐店创业计划书
2014/01/13 职场文书
毕业生就业推荐表自我鉴定
2014/03/20 职场文书
部门群众路线教育实践活动对照检查材料思想汇报
2014/10/07 职场文书
见习报告的格式
2014/10/31 职场文书
行政经理岗位职责
2015/04/15 职场文书
什么是求职信?求职信应包含哪些内容?
2019/08/14 职场文书
《女娲补天》读后感5篇
2019/12/31 职场文书
php远程请求CURL案例(爬虫、保存登录状态)
2021/04/01 PHP
世界各国短波电台对东亚播送时间频率表(SW)
2021/06/28 无线电
进行数据处理的6个 Python 代码块分享
2022/04/06 Python