上篇文章,咱們講到了mergeOptions的部分實現,今天接着前面的部分講解,來看代碼,若是你們以爲看講解枯燥能夠直接翻到本文的最後看mergeOptions的整個流程圖。vue
const extendsFrom = child.extends if (extendsFrom) { parent = mergeOptions(parent, extendsFrom, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm) } }
這段代碼的處理的邏輯是,當傳入的options裏有mixin或者extends屬性時,再次調用mergeOptions方法合併mixins和extends裏的內容到實例的構造函數options上(即parent options)好比下面這種狀況ide
const childComponent = Vue.component('child', { ... mixins: [myMixin], extends: myComponent ... }) const myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin') } } } const myComponent = { mounted: function () { this.goodbye() }, methods: { goodbye: function () { console.log('goodbye from mixin') } } }
就會把傳入的mounted, created鉤子處理函數,還有methods方法提出來去和parent options作合併處理。
弄明白了這點咱們繼續回到mergeOptions的代碼函數
const options = {} let key
變量options存儲合併以後的options,變量key存儲parent options和child options上的key值。
接下來的部分算是mergeOptions方法的核心處理部分了,像炒菜同樣,前面的代碼至關於把全部的菜都配好了。接下來的部分就是教你怎麼去炒菜了。this
for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) }
前兩段for循環代碼大同小異,都是遍歷options上的key值,而後調用mergeField方法來處理options。mergeField方法中出現了一個變量strats和defaultStrat。這兩個變量存儲的就是咱們的合併策略,也就是炒菜的菜譜,咱們先來看看defaultStratspa
const defaultStrat = function (parentVal: any, childVal: any): any { return childVal === undefined ? parentVal : childVal }
defaultStrat的邏輯是,若是child上該屬性值存在時,就取child上的該屬性值,若是不存在,則取parent上的該屬性值。如今咱們知道默認的合併策略是什麼了,接下來看其餘的合併策略。咱們來看看strats裏都有哪些屬性?
上圖就是strats中全部的策略了。粗略看起來裏面的內容很是的多,若是細細分析會發現,其實總結起來無非就是幾種合併策略。下面分別爲你們介紹code
全部關於鉤子函數的策略,其實都是調用mergeHook方法。component
function mergeHook ( parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function> ): ?Array<Function> { return childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal }
mergeHook採用了一個很是騷的嵌套三元表達式來控制最後的返回值。下面咱們來解析這段三元表達式
(1) child options上不存在該屬性,parent options上存在,則返回parent上的屬性。對象
(2)child和parent都存在該屬性,則返回concat以後的屬性blog
(3)child上存在該屬性,parent不存在,且child上的該屬性是Array,則直接返回child上的該屬性遞歸
(4) child上存在該屬性,parent不存在,且child上的該屬性不是Array,則把該屬性先轉換成Array,再返回。
上面就是鉤子函數合併策略,結合圖片看應該會比較清晰。
介紹完了鉤子函數的合併策略,咱們接下來看props,methods,inject,computed等屬性的合併策略。
strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { if (childVal && process.env.NODE_ENV !== 'production') { assertObjectType(key, childVal, vm) } if (!parentVal) return childVal const ret = Object.create(null) extend(ret, parentVal) if (childVal) extend(ret, childVal) return ret }
這個合併方法邏輯很簡單,若是child options上這些屬性存在,則先判斷它們是否是對象。
(1)若是parent options上沒有該屬性,則直接返回child options上的該屬性
(2)若是parent options和child options都有,則合併parent options和child options並生成一個新的對象。(若是parent和child上有同名屬性,合併後的以child options上的爲準)
components/directives/filters這幾個屬性的處理邏輯以下
function mergeAssets ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): Object { const res = Object.create(parentVal || null) if (childVal) { process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm) return extend(res, childVal) } else { return res } }
這裏的處理邏輯和上一種狀況的相似,這裏不作過多講解。
data和provide的策略相對來講複雜一些,咱們先來看代碼
export function mergeDataOrFn ( parentVal: any, childVal: any, vm?: Component ): ?Function { if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } } else { return function mergedInstanceDataFn () { // instance merge const instanceData = typeof childVal === 'function' ? childVal.call(vm, vm) : childVal const defaultData = typeof parentVal === 'function' ? parentVal.call(vm, vm) : parentVal if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData } } } }
這個合併策略能夠分紅兩種狀況來考慮。
第一種狀況,當前調用mergeOptions操做的是vm實例(調用new新建vue實例觸發mergeOptions方法)
return function mergedInstanceDataFn () { // instance merge const instanceData = typeof childVal === 'function' ? childVal.call(vm, vm) : childVal const defaultData = typeof parentVal === 'function' ? parentVal.call(vm, vm) : parentVal if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData } }
若是新建實例時傳入的options上有data屬性,則調用mergeData方法合併實例上的data屬性和其構造函數options上的data屬性(若是有的話)
第二種狀況,當前調用mergeOptions操做的不是vm實例(即經過Vue.extend/Vue.component調用了mergeOptions方法)
if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } }
在這種狀況下,其處理邏輯也是相似的。若是當前實例options或者構造函數options上有一個沒有data屬性,則返回另外一個的data屬性,若是二者都有,則一樣調用mergeData方法處理合並。
既然這兩種狀況都調用了mergeData方法,那咱們就繼續來看看mergeData的源碼
function mergeData (to: Object, from: ?Object): Object { if (!from) return to let key, toVal, fromVal const keys = Object.keys(from) for (let i = 0; i < keys.length; i++) { key = keys[i] toVal = to[key] fromVal = from[key] if (!hasOwn(to, key)) { set(to, key, fromVal) } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { mergeData(toVal, fromVal) } } return to }
mergeData的邏輯是,若是from對象中有to對象裏沒有的屬性,則調用set方法,(這裏的set就是Vue.$set,先能夠簡單理解爲對象設置屬性。以後會細講)若是from和to中有相同的key值,且key對應的value是對象,則會遞歸調用mergeData方法,不然以to的值爲準,最後返回to對象。這裏咱們就講完了data的合併策略。
返回mergeOptions代碼裏,在通過這幾種合併策略合併options後,最終返回options
return options
講到這裏,整個mergeOptions的流程也講完了。這個方法牽扯到的內容比較多,流程也比較複雜。爲了你們更好的理解和記憶。我畫了一張圖來表達整個mergeOptions的過程。
若是你們以爲個人文章寫的還行,請爲我點贊,大家的承認是我最大的動力。