VUE-這回真的要徹底理解Vue選項Props

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

VUE-這回真的要徹底理解Vue選項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


分享到:


相關文章: