provide / inject
应用场景
- 父子组件通讯:替代this.$parent
- 祖孙组件通讯:
祖先组件
不需要知道哪些后代组件在使用他提供的数据;后代组件
也不需要知道注入的数据来自哪里
使用方法
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
指定默认值
const Child = {
inject: {
foo: { default: 'foo' }
}
}
指定来源
const Child = {
inject: {
foo: {
from: 'bar',
default: 'foo'
}
}
}
非原始值
(对象类型,数组类型…) ==> 需要指定工厂方法
const Child = {
inject: {
foo: {
from: 'bar',
default: () => [1, 2, 3]
},
boo: {
from: 'user',
default: () => {name: 'micke'}
},
}
}
原理
provide api原理
/**
* 当前组件实例的provide首先会指向父组件的provides
*/
const instance = {
provide: parent ? parent.provides : Object.create(appContent.provides)
// 其他属性
// ...
}
/**
* provide api
* @param {String} key
* @param {Any} value 设定值
*/
function provide(key, value) {
let provides = currentInstance.provides // 当前组件的provides
const parentProvides = currentInstance.parent && currentInstance.parent.provides // 父组件的provides
// 第一次调用provide api,会进入这个if,provide 和 inject 绑定并不是可响应的。这是刻意为之的
if (parentProvides === provides) {
provides = currentInstance.provides = Object.create(parentProvides) // 新建一个空对象,并把父组件provides挂载到原型上(__proto__)
}
provides[key] = value // 设定值
}
- 组件的provide 默认等于父组件provide。
- 当组件配置了provide时,它就会自动创建一个新的空对象保存配置的provide,同时这个新对象的原型指向父组件的provide。
- 通过这种
原型链
机制,在孙组件就很容易访问到祖辈组件的provide
inject api 原理
/**
* inject api
* @param {String} key
* @param {Any} defaultValue 默认值
*/
function inject(key, defaultValue) {
const instance = currentInstance || currentRendingInstance // 当前实例
if (instance) {
const provides = instance.provides
if (key in provides) {
return provides[key] // 如果key存在,就返回
}
else if (arguments.length > 1) {
return defaultValue // key不存在,且设置了默认值,就返回默认值
}
else if (process.env.NODE_ENV !== 'production') {
warn(`injection "${String(key)}" not found.`) // 报错提示
}
}
}
- inject会尝试寻找目标key,包括provides的原型链上
- 没有找到key的还就返回默认值