Skip to content

Provide/Inject 的实现

让我们实现 Provide/Inject

这是 Provide 和 Inject 的实现.实现也相当简单.
基本概念是在 ComponentInternalInstance 中有一个地方来存储提供的数据(provides),并保持父组件的实例来继承数据.

需要注意的一点是,provide 有两个入口点.一个是在组件的 setup 期间,这很容易想象,
另一个是在 App 上调用 provide 时.

ts
const app = createApp({
  setup() {
    //.
    //.
    //.
    provide('key', someValue) // 这是从组件调用 provide 的情况
    //.
    //.
  },
})

app.provide('key2', someValue2) // 在 App 上提供

现在,我们应该在哪里存储 app 提供的内容?由于 app 不是组件,这是一个问题.

为了给你答案,让我们说 app 实例有一个名为 AppContext 的对象,我们将在其中存储 provides 对象.

将来,我们将向这个 AppContext 添加全局组件和自定义指令设置.

现在我们已经解释了到目前为止的一切,让我们实现代码,使其按以下方式工作!

※ 假设的签名

ts
export interface InjectionKey<_T> extends Symbol {}

export function provide<T, K = InjectionKey<T> | string | number>(
  key: K,
  value: K extends InjectionKey<infer V> ? V : T,
)

export function inject<T>(key: InjectionKey<T> | string): T | undefined
export function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
ts
const Child = {
  setup() {
    const rootState = inject<{ count: number }>('RootState')
    const logger = inject(LoggerKey)

    const action = () => {
      rootState && rootState.count++
      logger?.('Hello from Child.')
    }

    return () => h('button', { onClick: action }, ['action'])
  },
}

const app = createApp({
  setup() {
    const state = reactive({ count: 1 })
    provide('RootState', state)

    return () =>
      h('div', {}, [h('p', {}, [`${state.count}`]), h(Child, {}, [])])
  },
})

type Logger = (...args: any) => void
const LoggerKey = Symbol() as InjectionKey<Logger>

app.provide(LoggerKey, window.console.log)

到此为止的源代码:
chibivue (GitHub)

基于 MIT 许可证发布。