摘要:在解析完其構造函數上的之后,需要把構造函數上的和實例化時傳入的進行合并操作并生成一個新的。檢查組件名稱是否合法首先看傳入的三個參數,,這三個參數分別代表的是該實例構造函數上的實例化時傳入的實例本身。
前幾篇文章中我們講到了resolveConstructorOptions,它的主要功能是解析當前實例構造函數上的options,不太明白的同學們可以看本系列的前幾篇文章。在解析完其構造函數上的options之后,需要把構造函數上的options和實例化時傳入的options進行合并操作并生成一個新的options。這個合并操作就是今天要講的mergeOptions。如果大家不想看枯燥的講解,可以直接點擊人人都能懂的Vue源碼系列—04—mergeOptions-下,翻到文章最后,查看整個mergeOptions的流程圖。
Merge two option objects into a new one.
Core utility used in both instantiation and inheritance.
先來看源碼中對mergeOptions方法的注釋。mergeOptions的功能是合并兩個options對象,并生成一個新的對象。是實例化和繼承中使用的核心方法??梢妋ergeOptions方法的重要性。既然這么重要,那我就帶大家一行一行的來解析代碼。保證大家看完這篇文章后,能徹底的理解mergeOptions的作用。
export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { if (process.env.NODE_ENV !== "production") { checkComponents(child) // 檢查組件名稱是否合法 } if (typeof child === "function") { child = child.options } normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child) 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) } } const options = {} let key 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) } return options }
首先看傳入的三個參數,parent,child,vm,這三個參數分別代表的是該實例構造函數上的options,實例化時傳入的options,vm實例本身。結合Vue作者寫的注釋,我們明白了,原來mergeoptions方法是要合并構造函數和傳入的options這兩個對象。
明白了這點之后,接下來往下看
if (process.env.NODE_ENV !== "production") { checkComponents(child) // 檢查組件名稱是否合法 }
這段代碼主要是判斷當前環境是不是生產環境,如果不是,則調用checkComponents方法來檢查組件名稱是否是可用名稱。我們來看看checkComponents的邏輯。
function checkComponents (options: Object) { for (const key in options.components) { validateComponentName(key) } } export function validateComponentName (name: string) { if (!/^[a-zA-Z][w-]*$/.test(name)) { warn( "Invalid component name: "" + name + "". Component names " + "can only contain alphanumeric characters and the hyphen, " + "and must start with a letter." ) } if (isBuiltInTag(name) || config.isReservedTag(name)) { warn( "Do not use built-in or reserved HTML elements as component " + "id: " + name ) } }
如果child的options(實例化傳入的options)有components屬性。如下面這種情況
const app = new Vue({ el: "#app", ... components: { childComponent } ... })
那么就調用validateComponentName來驗證傳入的組件名稱是否符合以下特征。
包含數字,字母,下劃線,連接符,并且以字母開頭
是否和html標簽名稱或svg標簽名稱相同
是否和關鍵字名稱相同,如undefined, infinity等
如果滿足第一條,并且第2,3條都是不相同的話,那么組件名稱可用。
我們再回到mergeOptions源碼中
if (typeof child === "function") { child = child.options }
如果child是function類型的話,我們取其options屬性作為child。
接下來看這三個方法以normalize開頭的方法
normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child)
這三個方法的功能類似,分別是把options中的props,inject,directives屬性轉換成對象的形式。因為有些傳入的時候可能會是數組的形式。如
Vue.component("blog-post", { props: ["postTitle"], template: "normalizeProps{{ postTitle }}
" })
我們先來看props處理的邏輯
function normalizeProps (options: Object, vm: ?Component) { const props = options.props if (!props) return const res = {} let i, val, name if (Array.isArray(props)) { i = props.length while (i--) { val = props[i] if (typeof val === "string") { name = camelize(val) res[name] = { type: null } } else if (process.env.NODE_ENV !== "production") { warn("props must be strings when using array syntax.") } } } else if (isPlainObject(props)) { for (const key in props) { val = props[key] name = camelize(key) res[name] = isPlainObject(val) ? val : { type: val } } } else if (process.env.NODE_ENV !== "production") { warn( `Invalid value for option "props": expected an Array or an Object, ` + `but got ${toRawType(props)}.`, vm ) } options.props = res }
首先明確這兩個方法里的參數是什么,options傳入的是child,即實例化時傳入的options。vm是實例。知道了這兩個參數是什么,我們繼續來研究代碼。
const props = options.props if (!props) return const res = {} let i, val, name
上面的代碼主要是聲明一些變量。res用來存放修改后的props,最后把res賦給新的props。下面的邏輯可以分為兩種情況來考慮
props是數組當props是數組的時候,如下面這種情況
Vue.component("blog-post", { props: ["postTitle"], template: "{{ postTitle }}
" })
它的處理邏輯是,遍歷props數組,把數組的每一項的值作為res對象的key,value值等于{type: null},即把上面例子中的["postTitle"]轉換成下面這種形式
{ postTitle: { type: null } }props是對象
當props是對象時,如下面這種情況
Vue.component("my-component", { props: { // 必填的字符串 propC: { type: String, required: true } } })
這種情況的處理邏輯是遍歷對象,先把對象的key值轉換成駝峰的形式。然后再判斷對象的值,如果是純對象(即調用object.prototype.toString方法的結果是[object Object]),則直接把對象的值賦值給res,如果不是,則把{ type: 對象的值}賦給res。最終上面這種形式會轉換成
{ propC: { type: String, required: true } }
如果傳入的props不是純對象也不是數組,且當前環境也不是生產環境,則拋出警告。
warn( `Invalid value for option "props": expected an Array or an Object, ` + `but got ${toRawType(props)}.`, vm )
最后,把處理過的props重新賦值給options.props。
normalizeInject這個方法的邏輯和normalizeProps類似,主要是處理inject。inject屬性如果大家平時不是寫庫或者插件的話,可能很少接觸到,可以先查看inject的使用,inject的傳入和props類似。可以傳入object,也可以傳入array
// array var Child = { inject: ["foo"], created () { console.log(this.foo) // => "bar" } // ... } // object const Child = { inject: { foo: { from: "bar", default: "foo" } } }
由于這個方法和normalizeProps邏輯基本一樣,這里也不具體展開講了。上面的demo最終會被轉換成如下形式
// array { foo: { from: "foo"} } // object { foo: { from: "bar", default: "foo" } }normalizeDirectives
這個方法主要是處理一些自定義指令,如果不了解自定義指令的同學可以自定義指令。這里的方法處理邏輯主要針對自定義指令中函數簡寫的情況。如下
Vue.directive("color", function (el, binding) { el.style.backgroundColor = binding.value })
normalizeDirectives構造函數會把這個指令傳入的參數,最終轉換成下面這種形式
color: { bind: function (el, binding) { el.style.backgroundColor = binding.value }, update: function (el, binding) { el.style.backgroundColor = binding.value } }
由于文章篇幅所限,本篇文章先講解到這里,下篇繼續帶大家來看mergeOptions的實現。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107757.html
摘要:上一篇文章中說道,函數要分兩種情況進行說明,第一種是為基礎構造器的情況,這個已經向大家介紹過了,今天這篇文章主要介紹第二種情況,是創建的子類。表示的是當前構造器上新增的,表示的是當前構造器上新增的封裝。 上一篇文章中說道,resolveConstructorOptions函數要分兩種情況進行說明,第一種是Ctor為基礎構造器的情況,這個已經向大家介紹過了,今天這篇文章主要介紹第二種情況...
摘要:上一篇文章中說道,函數要分兩種情況進行說明,第一種是為基礎構造器的情況,這個已經向大家介紹過了,今天這篇文章主要介紹第二種情況,是創建的子類。表示的是當前構造器上新增的,表示的是當前構造器上新增的封裝。 上一篇文章中說道,resolveConstructorOptions函數要分兩種情況進行說明,第一種是Ctor為基礎構造器的情況,這個已經向大家介紹過了,今天這篇文章主要介紹第二種情況...
摘要:下面分別為大家介紹鉤子函數的策略所有關于鉤子函數的策略,其實都是調用方法。的策略介紹完了鉤子函數的合并策略,我們接下來看等屬性的合并策略。如果當前實例或者構造函數上有一個沒有屬性,則返回另一個的屬性,如果兩者都有,則同樣調用方法處理合并。 上篇文章,我們講到了mergeOptions的部分實現,今天接著前面的部分講解,來看代碼,如果大家覺得看講解枯燥可以直接翻到本文的最后看mergeO...
摘要:上篇文章介紹了構造函數的部分實現,當前實例不是組件時,會執行方法。這個文件就是對構造函數進行的第一層包裝了。但是注意這里的代碼我們構造函數的第二層包裝,就在這個文件里了?;氐降脑创a中,當不存在時,直接返回基礎構造器的。 上篇文章介紹了Vue構造函數的部分實現,當前Vue實例不是組件時,會執行mergeOptions方法。 vm.$options = mergeOptions( re...
摘要:果然我們找到了的構造函數定義。告訴你是一個構造函數,需要用操作符去調用。在深入方法之前,我們先把目光移到文件里在的構造函數定義之后,有一系列方法會被立即調用。下篇博文主要介紹相關的內容,涉及到原型鏈和構造函數以及部分的實現,敬請期待 上篇博文中說到了Vue源碼的目錄結構是什么樣的,每個目錄的作用我們應該也有所了解。我們知道core/instance目錄主要是用來實例化Vue對象,所以我...
閱讀 966·2023-04-26 02:49
閱讀 1177·2021-11-25 09:43
閱讀 2549·2021-11-18 10:02
閱讀 2923·2021-10-18 13:32
閱讀 1285·2019-08-30 13:54
閱讀 2081·2019-08-30 12:58
閱讀 3015·2019-08-29 14:06
閱讀 2155·2019-08-28 18:10