Vue 中 provide 和 reject 的使用详解和源码解析

Vue 提供了provide 和reject 属性为高阶组件提供了便利的数据传递。

一:使用详解

provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }

完整的用法

// 父级组件提供 'foo'
var Provider = {
	data () {
		return  {
			x: 'test'
		}
	}
	// 可以直接使用对象,对象包含需要提供的key:“value”
	provide: {
	    foo: 'bar'
	},
    // 可是设置provide 为一个函数,函数返回一个provide属性,this指向vue实例,所以可以调用组件data里的值得
    provide () {
	   return {
	        a: this.x
	   }
   },
	  // ...
}

// 子组件注入 'foo'
var Child = {
  // inject可以是一个数组,数组里包含需要注入的key的字面量
  inject: ['foo'],
  // inject 可以是一个对象
  inject: {
  	foo: 'foo'
  },
  // inject 可以设置默认值,所以可以inject的指可以在provide里不存在
  inject: {
	foo: {default: 'default value'}
  },
  // default 值也可是一个有返回值的函数
  inject: {
	foo: {default: () => {return [1,2,3]}}
  },
  // 完整的带默认值的写法, a 是provide中提供的key的字面量
  inject: {
	foo: {
		from: 'a',
		default: 'default value'
   }
  },
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

二:源码解读

// 初始化组件的时候,初始话provide属性的方法
function initProvide (vm) {
    var provide = vm.$options.provide;
    if (provide) {
      vm._provided = typeof provide === 'function' // 判断如果传入的方法是provide是函数,执行函数获取到返回的对象,否则直接返回
        ? provide.call(vm)
        : provide;
    }
  }
// vue中处理inject  属性
  function initInjections (vm) {
    var result = resolveInject(vm.$options.inject, vm); // 见下一个函数
    if (result) {
      toggleObserving(false); // 关闭响应式数据定义开关,保证在调用 defineReactive$$1 的时候不对数据进行响应式绑定
      Object.keys(result).forEach(function (key) {
        /* istanbul ignore else */
        {
          defineReactive$$1(vm, key, result[key], function () { // 这里是保证inject属性不能设置成响应式的,否则会发出警告
            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) {
    if (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)
        : Object.keys(inject);

      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        // #6574 in case the inject object is observed...
        if (key === '__ob__') { continue }
        var provideKey = inject[key].from;
        var source = vm;
        // 循环向上寻找父节点,直到找到包含有inject key的provide对象
        while (source) {
          if (source._provided && hasOwn(source._provided, provideKey)) {
            result[key] = source._provided[provideKey];
            break
          }
          source = source.$parent;
        }
        if (!source) { // 如果发现父或者父的父没有inject里的key,
          if ('default' in inject[key]) { // 就去找设置的default的值
            var provideDefault = inject[key].default;
            result[key] = typeof provideDefault === 'function'
              ? provideDefault.call(vm)
              : provideDefault;
          } else {
            warn(("Injection \"" + key + "\" not found"), vm);
          }
        }
      }
      return result
    }
  }

可以发现,通过这种方式,我们又为组件提供了一种从父向子传递数据的一种方式,而且不一定是父子,爷孙组件也可以传递。但是官方不建议我们在业务组件中这么使用,因为如果通过这样的方式会使组件之间的耦合度变得很高,尤其是当用户滥用跨多级组件进行传值,代码的稳定性和可维护性将大大降低。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森哥的歌

一杯咖啡半包烟,助我熬过那长夜

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值