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 許可證發布。