原理
合并配置
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 }
}
}
}
判断props 是不是数组, 在进行驼峰化, 数组形式我们 type 都设置为null
初始化
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
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 ` +
`overwritten 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)
}
主要做三件事情, 校验props 生成响应式 defineReactive
// call factory function for non-Function types
// a value is Function if its prototype is function even across different execution context
return typeof def === 'function' && getType(prop.type) !== 'Function'
? def.call(vm)
: def
只针对对象和数组类型的 prop,要求必须是一个工厂函数,它的值是工厂函数返回的值,所以要执行。 对于函数类型的 prop,那么它的值就是这个函数本身,所以不需要执行。 如果直接返回对象,那么多个组件实例的相应的默认 prop 值会共享这个对象,一旦修改这个对象,则会影响所有实例,是不符合预期的。
子组件重新渲染 分为props发生改变 或者内部属性发生变动
在构造函数初始化过程中进行proxy
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
props 更新
子组件创建的时候会触发 prepatch 方法
prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
const options = vnode.componentOptions
const child = vnode.componentInstance = oldVnode.componentInstance
updateChildComponent(
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children
)
}
接着我们看 lifecycle 里面的
// update props
if (propsData && vm.$options.props) {
toggleObserving(false)
const props = vm._props
const propKeys = vm.$options._propKeys || []
for (let i = 0; i < propKeys.length; i++) {
const key = propKeys[i]
const propOptions: any = vm.$options.props // wtf flow?
props[key] = validateProp(key, propOptions, propsData, vm)
}
toggleObserving(true)
// keep a copy of raw propsData
vm.$options.propsData = propsData
}
因为data 初始化数据会保护props, 所以在extend 的时候 props 是不会参与初始化求值的, 所以不能在 extend 的时候 proxy
vue 的优化 只递归父 props 对象 defineReactive, 没有处理子 props。
toggleObserving 在响应式中的优化
defineReactive(props)是为了给值类型用哦? 值类型props变化触发重新渲染执行 patchVnode ==>updateComponent,改变值类型props值触发setter。 对象类型props属性变化不会触发父组件的重新渲染也就不会触发updateComponent,直接通过访问的对象属性dep通知子组件watcher更新了
最后更新于
这有帮助吗?