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 相关文章推荐
基于jQuery的仿flash的广告轮播代码
Nov 04 Javascript
js网页实时倒计时精确到秒级
Feb 10 Javascript
jQuery简单图表peity.js使用示例
May 02 Javascript
JavaScript中的this,call,apply使用及区别详解
Jan 29 Javascript
Javascript基础_标记文字的实现方法
Jun 14 Javascript
jQuery元素属性操作实例(设置、获取及删除元素属性)
Sep 08 Javascript
js通过classname来获取元素的方法
Nov 24 Javascript
详解在Vue中有条件地使用CSS类
Sep 30 Javascript
vue中的计算属性的使用和vue实例的方法示例
Dec 04 Javascript
Vue项目history模式下微信分享爬坑总结
Mar 29 Javascript
微信小程序 textarea 层级过高问题简单解决方案
Oct 14 Javascript
Vue提供的三种调试方式你知道吗
Jan 18 Vue.js
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 验证码(倾斜,正弦干扰线,黏贴,旋转)
2013/06/29 PHP
WAMP环境中扩展oracle函数库(oci)
2015/06/26 PHP
PHP新特性之字节码缓存和内置服务器
2017/08/11 PHP
用js脚本控制asp.net下treeview的NodeCheck的实现代码
2010/03/02 Javascript
一个简单的瀑布流效果(主体形式自写)
2013/05/27 Javascript
JS中的数组的sort方法使用示例
2014/01/22 Javascript
简单介绍JavaScript中字符串创建的基本方法
2015/07/07 Javascript
浅谈javascript 函数表达式和函数声明的区别
2016/01/05 Javascript
学习JavaScript事件流和事件处理程序
2016/01/25 Javascript
基于Vue 2.0的模块化前端 UI 组件库小结
2017/12/21 Javascript
element ui里dialog关闭后清除验证条件方法
2018/02/26 Javascript
使用VUE实现在table中文字信息超过5个隐藏鼠标移到时弹窗显示全部
2019/09/16 Javascript
原生JS实现汇率转换功能代码实例
2020/05/13 Javascript
Antd下拉选择,自动匹配功能的实现
2020/10/24 Javascript
node.js通过Sequelize 连接MySQL的方法
2020/12/28 Javascript
python中List的sort方法指南
2014/09/01 Python
详细解读Python的web.py框架下的application.py模块
2015/05/02 Python
django用户登录验证的完整示例代码
2019/07/21 Python
关于pandas的离散化,面元划分详解
2019/11/22 Python
Python实现隐马尔可夫模型的前向后向算法的示例代码
2019/12/31 Python
基于python requests selenium爬取excel vba过程解析
2020/08/12 Python
Python collections.deque双边队列原理详解
2020/10/05 Python
Django-simple-captcha验证码包使用方法详解
2020/11/28 Python
详解WebSocket跨域问题解决
2018/08/06 HTML / CSS
教师旷工检讨书
2014/01/18 职场文书
培训主管岗位职责
2014/02/01 职场文书
银行开业庆典方案
2014/02/06 职场文书
2014年乡镇植树节活动方案
2014/02/28 职场文书
幼儿教师暑期培训方案
2014/08/27 职场文书
法定授权委托证明书
2014/09/27 职场文书
2014年学校工作总结
2014/11/20 职场文书
2015年安全员工作总结范文
2015/04/22 职场文书
PHP对接阿里云虚拟号的实现(号码隐私保护)
2021/04/06 PHP
MySQL优化之如何写出高质量sql语句
2021/05/17 MySQL
python状态机transitions库详解
2021/06/02 Python
游戏《铁拳》动画化!2022年年内播出
2022/03/21 日漫