Vue 2源碼閱讀 Provide Inject 依賴註入詳解
Provide/Inject 初始化
1. initInjections 依賴初始化
該步驟其實發生在 initState 之前,但是由於 provide/inject 一般是配合使用,所以這裡調整瞭一下順序。
該函數的定義與過程都比較簡單:
export function initInjections(vm: Component) { const result = resolveInject(vm.$options.inject, vm) if (result) { toggleObserving(false) Object.keys(result).forEach(key => { if (__DEV__) { defineReactive(vm, key, result[key], () => warn('')) } else { defineReactive(vm, key, result[key]) } }) toggleObserving(true) } } export function resolveInject(inject: any, vm: Component): Record<string, any> | undefined | null { if (inject) { const result = Object.create(null) const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (key === '__ob__') continue const provideKey = inject[key].from if (provideKey in vm._provided) { result[key] = vm._provided[provideKey] } else if ('default' in inject[key]) { const provideDefault = inject[key].default result[key] = isFunction(provideDefault) ? provideDefault.call(vm) : provideDefault } else if (__DEV__) { warn('') } } return result } }
- 在 initInjections 函數中,隻是遍歷瞭 options.inject 配置的依賴數據,並 關閉 瞭依賴數據的 響應式依賴收集,最後通過 defineReactive 將對應的數據掛載到實例 vm 上,以便後面能直接訪問。
這就是官方提示的 為什麼 provide/inject 的數據不是響應式的瞭。
- 而 resolveInject 函數就是用來對組件的 inject 依賴數據進行處理,並返回一個沒有多餘原型鏈的對象。
在官方文檔中,inject 接收一個字符串數組或者一個 key 為 string 的對象,而作為對象時則 必須 有 from 字段來表示依賴數據的獲取指向,另外也接收一個 default 屬性作為降級時使用的默認值。
但是,在 mergeOptions 之後,會將 options.inject 轉為標準對象格式。
並且這裡並沒有對註入數據 provide[key] 進行處理,而是直接賦值;所以才有:如果你傳入瞭一個可監聽的對象,那麼其對象的 property 還是可響應的。
resolveInject() 函數就是解析標準格式 inject 配置,並將上層組件的 provide 的值或者 default 默認值綁定到函數返回對象中;如果這兩個都沒有,則會提示錯誤信息 “injection xx not found”
2. initProvide 註入數據初始化
初始化註入數據的過程也很簡單,整個過程其實與 initInjection 類似。其函數定義如下:
export function initProvide(vm: Component) { const provideOption = vm.$options.provide if (provideOption) { const provided = isFunction(provideOption) ? provideOption.call(vm) : provideOption if (!isObject(provided)) { return } const source = resolveProvided(vm) const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided) for (let i = 0; i < keys.length; i++) { const key = keys[i] Object.defineProperty( source, key, Object.getOwnPropertyDescriptor(provided, key)! ) } } } export function resolveProvided(vm: Component): Record<string, any> { const existing = vm._provided const parentProvides = vm.$parent && vm.$parent._provided if (parentProvides === existing) { return (vm._provided = Object.create(parentProvides)) } else { return existing } }
官方文檔中對 provide 配置項的說明是,可以是一個對象或者一個返回對象的函數。
- 所以這裡首先判斷瞭 options.provide 的類型並獲取到瞭結果,如果結果 不是對象則會直接退出。
- 然後,則是初始化 provide 的數據。
此時會將當前實例的 provided 數據與父組件實例的 provided 進行比較,如果相同,則返回一個 以父組件實例 provided 數據為原型創建的對象,否則直接返回當前實例的 provided 數據。
因為每一個實例都會進行與父組件實例的註入數據比較,所以才能多層級傳遞
- 最後,則是遍歷 provided 對象,通過 Object.defineProperty 來處理數據獲取。
總結
整個 provide/inject 的初始化過程都很清晰,隻是通過少數校驗和處理,將 provide 數據一層一層傳遞下去,直到 inject 依賴時讀該改數據的值;
並且因為在初始化時會關閉響應式處理部分,所以 provide/inject 的 直接綁定數據 才不支持響應式;但
又因為 沒有對數據的進行深層次處理,所以,原有的響應式數據才會繼續觸發整個響應式系統的改變。
以上就是Vue 2源碼閱讀 Provide Inject 依賴註入詳解的詳細內容,更多關於Vue Provide Inject 依賴註入的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Vue3中Provide / Inject的實現原理分享
- vue3中 provide 和 inject 用法及原理
- Vue3全局組件通信之provide / inject詳解
- Vue 中 provide和inject的使用
- vue中的inject用法及說明