代码运行结果
代码示例
<div id="app"></div>
<script>
let {
createApp,
reactive,
Fragment,
toRefs,
h,
getCurrentInstance,
provide,
inject
} = Vue
const MyCpn = {
setup(props, { emit, slots }) {
const state = inject('state')
return () => h('div', null, [state.count])
}
}
const App = {
setup() {
const state = reactive({ count: 0 })
provide('state', state)
setTimeout(() => {
state.count++
}, 2000)
},
render() {
return h(MyCpn, null)
}
}
createApp(App).mount('#app')
</script>
第一:mountComponent 挂载组件阶段, 创建每个组件实例的时候,每个子组件实例的 provides 属性,默认继承自父组件实例的 provides 属性。
const instance: ComponentInternalInstance = {
provides: parent ? parent.provides : Object.create(appContext.provides),
}
第二:调用 Provide 函数:
- 先获取到当前组件实例的 provides, 再获取到当前父亲组件实例的 provides。
- 第一次两个 provides 相同,所以会通过 Object.create() 再 parentProvides 的基础上创建子 provides (子对象的原型链指向父对象)
- 如果在子组件中多次 provide, 就会走到 else 逻辑,在子 provide 上进行赋值操作。
export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
if (!currentInstance) {
if (__DEV__) {
warn(`provide() can only be used inside setup().`)
}
} else {
let provides = currentInstance.provides
const parentProvides =
currentInstance.parent && currentInstance.parent.provides
if (parentProvides === provides) {
provides = currentInstance.provides = Object.create(parentProvides)
}
// TS doesn't allow symbol as index type
provides[key as string] = value
}
}
每个子组件都是将所有父组件的 provides 通过 Object.create 关联起来,再生成自己的 provides。
第二:调用 inject 函数
instance.parent.provides获取到 父组件的 Providesreturn provides[key as string]从 provides 中找到 key 属性对应的属性值,并返回。
export function inject(
key: InjectionKey<any> | string,
defaultValue?: unknown,
treatDefaultAsFactory = false
) {
// fallback to `currentRenderingInstance` so that this can be called in
// a functional component
const instance = currentInstance || currentRenderingInstance
if (instance) {
// #2400
// to support `app.use` plugins,
// fallback to appContext's `provides` if the instance is at root
const provides =
instance.parent == null
? instance.vnode.appContext && instance.vnode.appContext.provides
: instance.parent.provides
if (provides && (key as string | symbol) in provides) {
// TS doesn't allow symbol as index type
return provides[key as string]
} else if (arguments.length > 1) {
return treatDefaultAsFactory && isFunction(defaultValue)
? defaultValue.call(instance.proxy)
: defaultValue
} else if (__DEV__) {
warn(`injection "${String(key)}" not found.`)
}
} else if (__DEV__) {
warn(`inject() can only be used inside setup() or functional components.`)
}
}
子组件消费的是父组件的 provides ,不包括本组件的 provides。