寫文章不容易,點(diǎn)個(gè)贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】
如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧
【Vue原理】Mixins - 源碼版
今天探索的是 mixins 的源碼,mixins 根據(jù)不同的選項(xiàng)類型會(huì)做不同的處理
篇幅會(huì)有些長,你知道的,有很多種選項(xiàng)類型的嘛,但不是很難。只是涉及源碼難免會(huì)有些煩,
不過這篇文章也不是給你直接看的,是為了可以讓你學(xué)習(xí)源碼的時(shí)候提供微薄幫助而已
如果不想看源碼的,可以看我的白話版
【Vue原理】Mixin - 白話版
我們也是要帶著兩個(gè)問題開始
1、什么時(shí)候開始合并
2、怎么合并
如果你覺得排版難看,請(qǐng)點(diǎn)擊下面原文鏈接 或者 關(guān)注公眾號(hào)【神仙朱】
什么時(shí)候合并合并分為兩種
1、全局mixin 和 基礎(chǔ)全局options 合并
這個(gè)過程是先于你調(diào)用 Vue 時(shí)發(fā)生的,也是必須是先發(fā)生的。這樣mixin 才能合并上你的自定義 options
Vue.mixin = function(mixin) { this.options = mergeOptions( this.options, mixin ); return this };
基礎(chǔ)全局options 是什么?
就是 components,directives,filters 這三個(gè),一開始就給設(shè)置在了 Vue.options 上。所以這三個(gè)是最先存在全局options
Vue.options = Object.create(null); ["component","directive","filter"].forEach(function(type) { Vue.options[type + "s"] = Object.create(null); });
這一步,是調(diào)用 Vue.mixin 的時(shí)候就馬上合并了,然后這一步完成 以后,舉個(gè)栗子
全局選項(xiàng)就變成下面這樣,然后每個(gè)Vue實(shí)例都需要和這全局選項(xiàng)合并
2、全局options和 自定義options合并
在調(diào)用Vue 的時(shí)候,首先進(jìn)行的就是合并
function Vue(options){ vm.$options = mergeOptions( { 全局component, 全局directive, 全局filter 等....}, options , vm ); // ...處理選項(xiàng),生成模板,掛載DOM 等.... }
options 就是你自己傳進(jìn)去的對(duì)象參數(shù),然后跟 全局options 合并,全局options 是哪些,也已經(jīng)說過了
怎么合并上面的代碼一直出現(xiàn)一個(gè)函數(shù) mergeOptions,他便是合并的重點(diǎn)
來看源碼
1、mergeOptionsfunction mergeOptions(parent, child, vm) { // 遍歷mixins,parent 先和 mixins 合并,然后在和 child 合并 if (child.mixins) { for (var i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm); } } var options = {}, key; // 先處理 parent 的 key, for (key in parent) { mergeField(key); } // 遍歷 child 的key ,排除已經(jīng)處理過的 parent 中的key for (key in child) { if (!parent.hasOwnProperty(key)) { mergeField(key); } } // 拿到相應(yīng)類型的合并函數(shù),進(jìn)行合并字段,strats 請(qǐng)看下面 function mergeField(key) { // strats 保存著各種字段的處理函數(shù),否則使用默認(rèn)處理 var strat = strats[key] || defaultStrat; // 相應(yīng)的字段處理完成之后,會(huì)完成合并的選項(xiàng) options[key] = strat(parent[key], child[key], vm, key); } return options }
這段代碼看上去有點(diǎn)繞,其實(shí)無非就是
1、先遍歷合并 parent 中的key,保存在變量options
2、再遍歷 child,合并補(bǔ)上 parent 中沒有的key,保存在變量options
3、優(yōu)先處理 mixins ,但是過程跟上面是一樣的,只是遞歸處理而已
在上面實(shí)例初始化時(shí)的合并, parent 就是全局選項(xiàng),child 就是組件自定義選項(xiàng),因?yàn)?parent 權(quán)重比 child 低,所以先處理 parent 。
“公司開除程序猿,也是先開始作用較低。。”
重點(diǎn)其實(shí)在于 各式各樣的處理函數(shù) strat,下面將會(huì)一一列舉
2、defaultStrats這段函數(shù)言簡意賅,意思就是優(yōu)先使用組件的options
組件options>組件 mixin options>全局options
var defaultStrats= function(parentVal, childVal) { return childVal === undefined ? parentVal : childVal };3、data
我們先默認(rèn) data 的值是一個(gè)函數(shù),簡化下源碼 ,但是其實(shí)看上去還是會(huì)有些復(fù)雜
不過我們主要了解他的工作過程就好了
1、兩個(gè)data函數(shù) 組裝成一個(gè)函數(shù)
2、合并 兩個(gè)data函數(shù)執(zhí)行返回的 數(shù)據(jù)對(duì)象
strats.data = function(parentVal, childVal, vm) { return mergeDataOrFn( parentVal, childVal, vm ) }; function mergeDataOrFn(parentVal, childVal, vm) { return function mergedInstanceDataFn() { var childData = childVal.call(vm, vm) var parentData = parentVal.call(vm, vm) if (childData) { return mergeData(childData, parentData) } else { return parentData } } } function mergeData(to, from) { if (!from) return to var key, toVal, fromVal; var keys = Object.keys(from); for (var i = 0; i < keys.length; i++) { key = keys[i]; toVal = to[key]; fromVal = from[key]; // 如果不存在這個(gè)屬性,就重新設(shè)置 if (!to.hasOwnProperty(key)) { set(to, key, fromVal); } // 存在相同屬性,合并對(duì)象 else if (typeof toVal =="object" && typeof fromVal =="object) { mergeData(toVal, fromVal); } } return to }4、生命鉤子
把所有的鉤子函數(shù)保存進(jìn)數(shù)組,重要的是數(shù)組子項(xiàng)的順序
順序就是這樣
[ 全局 mixin - created, 組件 mixin-mixin - created, 組件 mixin - created, 組件 options - created ]
所以當(dāng)數(shù)組執(zhí)行的時(shí)候,正序遍歷,就會(huì)先執(zhí)行全局注冊(cè)的鉤子,最后是 組件的鉤子
function mergeHook(parentVal, childVal) { var arr; arr = childVal ? // concat 不只可以拼接數(shù)組,什么都可以拼接 ( parentVal ? // 為什么parentVal 是個(gè)數(shù)組呢 // 因?yàn)闊o論怎么樣,第一個(gè) parent 都是{ component,filter,directive} // 所以在這里,合并的時(shí)候,肯定只有 childVal,然后就變成了數(shù)組 parentVal.concat(childVal) : ( Array.isArray(childVal) ? childVal: [childVal] ) ) : parentVal return arr } strats["created"] = mergeHook; strats["mounted"] = mergeHook; // ... 等其他鉤子5、component、directives、filters
我一直覺得這個(gè)是比較好玩的,這種類型的合并方式,我是從來沒有在項(xiàng)目中使用過的
原型疊加
兩個(gè)對(duì)象并沒有進(jìn)行遍歷合并,而是把一個(gè)對(duì)象直接當(dāng)做另一個(gè)對(duì)象的原型
這種做法的好處,就是為了保留兩個(gè)相同的字段且能訪問,避免被覆蓋
學(xué)到了學(xué)到了.....反正我是學(xué)到了
strats.components= strats.directives= strats.filters = function mergeAssets( parentVal, childVal, vm, key ) { var res = Object.create(parentVal || null); if (childVal) { for (var key in childVal) { res[key] = childVal[key]; } } return res }
就是下面這種,層層疊加的原型
6、watchwatch 的處理,也是合并成數(shù)組,重要的也是合并順序,跟 生命鉤子一樣
這樣的鉤子
[ 全局 mixin - watch, 組件 mixin-mixin - watch, 組件 mixin - watch, 組件 options - watch ]
按照正序執(zhí)行,最后執(zhí)行的 必然是組件的 watch
strats.watch = function(parentVal, childVal, vm, key) { if (!childVal) { return Object.create(parentVal || null) } if (!parentVal) return childVal var ret = {}; // 復(fù)制 parentVal 到 ret 中 for (var key in parentVal) { ret[key] = parentVal[key]; } for (var key$1 in childVal) { var parent = ret[key$1]; var child = childVal[key$1]; if (!Array.isArray(parent)) { parent = [parent]; } ret[key$1] = parent ? parent.concat(child) : ( Array.isArray(child) ? child: [child] ); } return ret };7、props、computed、methods
這幾個(gè)東西,是不允許重名的,合并成對(duì)象的時(shí)候,不是你死就是我活
重要的是,以誰的為主?必然是組件options 為主了
比如
組件的 props:{ name:""}
組件mixin 的 props:{ name:"", age: "" }
那么 把兩個(gè)對(duì)象合并,有相同屬性,組件的 name 會(huì)替換 mixin 的name
trats.props = strats.methods = strats.inject = strats.computed = function(parentVal, childVal, vm, key) { if (!parentVal) return childVal var ret = Object.create(null); // 把 parentVal 的字段 復(fù)制到 ret 中 for (var key in parentVal) { ret[key] = parentVal[key]; } if (childVal) { for (var key in childVal) { ret[key] = childVal[key]; } } return ret };
其實(shí)在白話版里面,就已經(jīng)測(cè)試了很多例子,整個(gè)執(zhí)行的流程也描述很清楚了,這里就是放個(gè)源碼供參考
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/105382.html
摘要:而我覺得現(xiàn)在出一個(gè)白話版,是讓大家有興趣去研究源碼的時(shí)候,可以提前理清一下思路。相當(dāng)于封裝,提取公共部分。顯然,今天我不是來教大家怎么用的,怎么用看文檔就好了,我是講解生命的真諦內(nèi)部的工作原理。而這個(gè)不會(huì)合并,直接替換掉整個(gè)選項(xiàng) 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版...
摘要:其中的標(biāo)志位什么時(shí)候設(shè)置呢,是在相應(yīng)的鉤子觸發(fā)之后,具體看下面源碼怎么執(zhí)行鉤子呢沒錯(cuò),就是下面這個(gè)函數(shù)是自己傳入的等回調(diào)那是怎么用呢比如觸發(fā)就會(huì)這么調(diào)用很簡單不,直接拿到鉤子,然后遍歷執(zhí)行,綁定上下文對(duì)象。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 ...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之創(chuàng)建組件今天就要開啟我 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
摘要:正文距離第一篇組件庫文章發(fā)布已經(jīng)過去個(gè)月了,在此期間利用零零散散的時(shí)間持續(xù)更新組件庫,目前移動(dòng)端組件庫已經(jīng)更新大類基礎(chǔ)表單彈出層種組件供使用。鏈接組件庫從到開發(fā)心得主頁更改版版方案祝工作順利鄧文斌年月日正文 距離第一篇UI組件庫文章發(fā)布已經(jīng)過去3個(gè)月了,在此期間利用零零散散的時(shí)間持續(xù)更新owl-ui組件庫,目前owl-ui移動(dòng)端組件庫已經(jīng)更新3大類(基礎(chǔ)、表單、彈出層)9種組件(Button...
摘要:簡介最近有點(diǎn)小閑置,于是乎希望寫點(diǎn)東西,正好自己喜歡聽歌,便決定自己寫一個(gè)音樂的簡易版。核心文件則是在在這里使用統(tǒng)一管理頁面切換動(dòng)畫,歌曲播放狀態(tài),歌曲進(jìn)度等信息。所有對(duì)于歌曲的操作都通過來進(jìn)行全局管理,然后對(duì)相應(yīng)的變化做出全局改變。 Vue-QQMusic 簡介: 最近有點(diǎn)小閑置,于是乎希望寫點(diǎn)東西,正好自己喜歡聽歌,便決定自己寫一個(gè)QQ音樂的簡易版。順便進(jìn)一步加深下自己對(duì)移動(dòng)端的知...
閱讀 3186·2019-08-30 15:55
閱讀 2952·2019-08-30 13:46
閱讀 1454·2019-08-29 17:29
閱讀 3522·2019-08-29 11:08
閱讀 3445·2019-08-29 11:04
閱讀 1093·2019-08-28 18:20
閱讀 552·2019-08-26 13:37
閱讀 1334·2019-08-26 11:49