7

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

 2 years ago
source link: http://www.fly63.com/article/detial/10601
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

provider/inject:简单来说就是在父组件 provide 中提供变量,子组件 inject 中来注入,然后可以在子组件内部使用 provide 的变量

需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。

使用场景:

由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问父组件的数据

一:使用详解

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

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

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/10601


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK