最近都在寫Vue相關的文章,感興趣的可以看回我之前寫的。
作者文章總集
正文:吃下這條魚? - props
初始化
initProps
是如何執行的:
1.normalizeProps
: initProps
之前的規範化資料
normalizeProps
的程式碼有點長,這裡只列舉經過規範化後的prop
型別和結果
1.1 字串
props: ["data"]
// 規範化後
props: {
data:{
type: null
}
}
複製程式碼
1.2 物件
props: {
data1: {
type: String,
default: ''
}
data2: Number,
}
// 規範化後
props: {
data1: {
type: String,
default: ''
},
data2: {
type: Number
},
}
複製程式碼
2.initProps
: 處理props
原始碼分析如下:
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop direct....`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
複製程式碼
2.1 常量定義
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
複製程式碼
propsData
: 儲存著傳遞進來的props
的值props: 引用
vm._props`,並初始化為{}keys
: 在vm.$options
上新增_propKeys
屬性isRoot
: 判斷是否存在vm.$parent
,若無則為根節點
2.2 條件判斷及迴圈
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
// 省略...
}
toggleObserving(true)
複製程式碼
!isRoot
:若當前例項非根節點,關閉toggleObserving
toggleObserving
: 可以理解為資料觀測的開關for...in
: 遍歷propsOptions
2.2.1: 遍歷propsOptions
做什麼
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwri tten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
}
複製程式碼
劃重點:
propsOptions
即opts.props
key
就是每個prop
的名字
此時進入迴圈:
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
複製程式碼
- 將
key
新增到vm.$options._propKeys
value
: 用validateProp
校驗是否為預期的型別值,然後返回相應 prop 值(或default值)
2.2.2: 接著進入 if...else
:
這裡註釋一下:
if (process.env.NODE_ENV !== 'production') {
// 駝峰轉連字元
const hyphenatedKey = hyphenate(key)
// 校驗prop是否為內建的屬性
// 內建屬性:key,ref,slot,slot-scope,is
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
// 子元件直接修改屬性時 彈出警告
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwri tten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
複製程式碼
最後簡化:
if (process.env.NODE_ENV !== 'production') {
// 駝峰轉連字元
// 校驗prop是否為內建的屬性
// 內建屬性:key,ref,slot,slot-scope,is
// 若是內建,彈出警告
defineReactive(props, key, value, () => {
// 子元件直接修改屬性時 彈出警告
} else {
defineReactive(props, key, value)
}
複製程式碼
工具函式: 「從原始碼中學習」Vue原始碼中的JS騷操作
2.2.3: defineReactive
: 最終處理
defineReactive(props, key, value)
複製程式碼
defineReactive
是老熟人了,但這裡要注意一點:
先前toggleObserving(false)
,關閉了觀測的開關,所以defineReactive
中呼叫 observe
, 是一個無效呼叫。
此時到這裡,可以得出一個結論
props
是通過 defineReactive
定義的,此時雖然是響應式資料,但沒有進行深度定義。
即,父元件傳給子元件props後,子元件不必再重複觀測props
2.2.4 toggleObserving(true)`: 最後開啟觀測開關
toggleObserving(true)
複製程式碼
重新開啟觀測開關,避免影響後續程式碼執行。
感悟:相比分析原始碼,理解後寫成部落格更難。用文字講清楚一件事可比敲程式碼難多了。
求一份深圳的內推
目前本人在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的深圳前端崗位!
- 微信:
huab119
- 郵箱:
454274033@qq.com