從vue源碼看props的用法
前言
平時(shí)寫vue的時(shí)候知道 props 有很多種用法,今天我們來看看vue內(nèi)部是怎么處理 props 中那么多的用法的。
vue提供的props的用法
1. 數(shù)組形式
props: ['name', 'value']
2. 對(duì)象形式
對(duì)象形式內(nèi)部也提供了三種寫法:
props: { // 基礎(chǔ)的類型檢查 name: String, // 多個(gè)可能的類型 value: [String, Number], // 對(duì)象形式 id: { type: Number, required: true } }
props實(shí)現(xiàn)的原理
function normalizeProps (options: Object, vm: ?Component) { const props = options.props if (!props) return const res = {} let i, val, name if (Array.isArray(props)) { ... } else if (isPlainObject(props)) { ... } else if (process.env.NODE_ENV !== 'production') { ... } options.props = res }
normalizeProps 函數(shù)就是vue實(shí)際處理 props 的地方,從函數(shù)名的翻譯我們可以看出該函數(shù)的功能就是標(biāo)準(zhǔn)化 props 的值。該函數(shù)主要分成3部分:① 從 options 對(duì)象中獲取 props 的值并且定義一個(gè)res空對(duì)象;②幾個(gè) if ... else ,分別根據(jù) props 值的不同類型來處理 res 對(duì)象;③ 用處理后的 res 對(duì)象覆蓋原來 options 對(duì)象的 props 屬性的值。
接下來看看那幾個(gè) if ... else 的代碼:
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.') } } }
這個(gè)代碼實(shí)際就是處理props的值為數(shù)組的情況,例如: props: ['name', 'value'] 。使用while遍歷該數(shù)組,如果數(shù)組內(nèi)元素的類型不是字符串并且不是生產(chǎn)環(huán)境,那么就拋錯(cuò):‘props的值類型為數(shù)組時(shí),數(shù)組里面的元素的類型就必須是字符串'。如果是字符串的情況下,使用 camelize 函數(shù)處理一下 val 的值,并且賦值給 name 變量。這里的 camelize 函數(shù)的實(shí)際作用就是將 '-' 轉(zhuǎn)換為駝峰。 camelize 函數(shù)具體的實(shí)現(xiàn)方式在后面分析。然后在 res 對(duì)象上面添加一個(gè)為 name 變量的屬性,該屬性的值為空對(duì)象 { type: null } 。
props: ['name', 'value'] 這種寫法經(jīng)過上面的處理后就會(huì)變成了下面這樣:
props: { name: { type: null }, value: { type: null } }
接下來看看下面這個(gè) else if(isPlainObject(props)) ,這里的 isPlainObject 函數(shù)實(shí)際就是返回 props 的值是否為 object , isPlainObject 函數(shù)的具體實(shí)現(xiàn)我們也在后面分析。
else if (isPlainObject(props)) { for (const key in props) { val = props[key] name = camelize(key) res[name] = isPlainObject(val) ? val : { type: val } } }
使用 for...in 遍歷props對(duì)象,和上面一樣使用 camelize 函數(shù)將 '-' 轉(zhuǎn)換為駝峰。這里有個(gè)三目運(yùn)算:
res[name] = isPlainObject(val) ? val : { type: val }
判斷了一下 val 如果是 object ,那么在res對(duì)象上面添加一個(gè)為name變量的屬性,并且將該屬性的值設(shè)置為val。這個(gè)其實(shí)就是處理下面這種props的寫法:
props: { // 對(duì)象形式 id: { type: Number, required: true } }
如果 val 不是 object ,那么也在res對(duì)象上面添加一個(gè)為name變量的屬性,并且將該屬性的值設(shè)置為{ type: val }。這個(gè)其實(shí)就是處理下面這種props的寫法:
props: { // 基礎(chǔ)的類型檢查 name: String, // 多個(gè)可能的類型 value: [String, Number], }
經(jīng)過處理后props會(huì)變成了下面這樣:
props: { name: { type: String }, value: { type: [String, Number] } }
所以不管我們使用vue提供的 props 哪種寫法,最終vue都會(huì)幫我們轉(zhuǎn)換成下面這種類型:
props: { name: { ..., type: '類型' } }
接下來看看上面提到的util函數(shù) isPlainObject ,先把源碼貼出來。
const _toString = Object.prototype.toString export function isPlainObject (obj: any): boolean { return _toString.call(obj) === '[object Object]' }
其實(shí) Object.prototype.toString.call(obj) 的值為obj對(duì)象的類型。例如:
Object.prototype.toString.call({a: 1}) // [object Object] Object.prototype.toString.call(new Date) // [object Date] Object.prototype.toString.call([1]) // [object Array] Object.prototype.toString.call(null) // [object Null]
接下來看看上面提到的util函數(shù) camelize ,還是先把源碼貼出來。
export function cached<F: Function> (fn: F): F { const cache = Object.create(null) return (function cachedFn (str: string) { const hit = cache[str] return hit || (cache[str] = fn(str)) }: any) } const camelizeRE = /-(\w)/g export const camelize = cached((str: string): string => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') })
這里定義了兩個(gè)函數(shù),分別是 cached 和 camelize ,其中 camelize 就是我們上面調(diào)用的, cached 是在 camelize 函數(shù)內(nèi)部調(diào)用的。
我們先來看看 camelize 函數(shù),其實(shí) camelize 函數(shù)就是執(zhí)行 cached 后返回的一個(gè)函數(shù)。調(diào)用 cached 時(shí)傳入了一個(gè)箭頭函數(shù),箭頭函數(shù)內(nèi)部是調(diào)用了正則的 replace 方法,將傳入的 str 變量中匹配 /-(\w)/g 的變成大寫字母,并且返回 replace 后的值。(也就是將 - 轉(zhuǎn)換成駝峰)。
再來看看 cached 函數(shù),該函數(shù)傳入的變量其實(shí)就是 camelize 那里的箭頭函數(shù),首先定義了一個(gè) cache 空對(duì)象,然后直接返回了 cachedFn 函數(shù)。我們?cè)谕獠空{(diào)用 camelize(key) 時(shí),其實(shí)就是執(zhí)行了這里的了 cachedFn 函數(shù), str 的值就是傳入的 key 的值。很明顯這里是一個(gè)閉包,可以在外部調(diào)用 camelize 函數(shù)的時(shí)候可以修改或者讀取這里定義的 cache 對(duì)象的值。獲取 cache 對(duì)象中 key 為 str 變量值的屬性值賦值給 hit 變量。如果有hit變量的值,那么就直接返回hit的值,如果沒有就執(zhí)行 camelize 傳入的箭頭函數(shù),并且將箭頭函數(shù)的返回值賦值給 catche 對(duì)象的 str 屬性。如果下次調(diào)用 camelize 函數(shù)時(shí)傳入了相同的 str ,那么就不會(huì)執(zhí)行箭頭函數(shù),直接返回閉包中的 cache 對(duì)象的 str 屬性的值。這里是性能優(yōu)化的一種手段。
例如:第一次調(diào)用 camelize('name') 后, cache 對(duì)象的值就變成了{(lán)name: 'name'}。然后在其他地方再次調(diào)用 camelize('name') 時(shí)再次執(zhí)行 cachedFn 函數(shù),此時(shí) hit 變量的值為'name'。直接返回 hit 變量的值,不會(huì)執(zhí)行傳入的箭頭函數(shù)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue3中同時(shí)定義多個(gè)插槽的實(shí)現(xiàn)示例
本文主要介紹了Vue3中同時(shí)定義多個(gè)插槽的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12Vue3 Composition API的使用簡(jiǎn)介
這篇文章主要介紹了Vue3 Composition API的使用簡(jiǎn)介,幫助大家更好的理解和學(xué)習(xí)使用vue,感興趣的朋友可以了解下2021-03-03vue+electron實(shí)現(xiàn)創(chuàng)建多窗口及窗口間的通信(實(shí)施方案)
這篇文章主要介紹了vue+electron實(shí)現(xiàn)創(chuàng)建多窗口及窗口間的通信,本文給大家分享實(shí)施方案結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09vue項(xiàng)目本地開發(fā)使用Nginx配置代理后端接口問題
這篇文章主要介紹了vue項(xiàng)目本地開發(fā)使用Nginx配置代理后端接口問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12基于vue+echarts數(shù)據(jù)可視化大屏展示的實(shí)現(xiàn)
這篇文章主要介紹了基于vue+echarts數(shù)據(jù)可視化大屏展示的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12ant design vue動(dòng)態(tài)循環(huán)生成表單以及自定義校驗(yàn)規(guī)則詳解
這篇文章主要介紹了ant design vue動(dòng)態(tài)循環(huán)生成表單以及自定義校驗(yàn)規(guī)則詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01vue使用exif獲取圖片旋轉(zhuǎn),壓縮的示例代碼
這篇文章主要介紹了vue使用exif獲取圖片旋轉(zhuǎn),壓縮的示例代碼,幫助大家更好的利用python處理圖片,感興趣的朋友可以了解下2020-12-12