Vue提倡单项数据流,主要是项目越来越复杂时,组件互相影响加上复用会使debug 的成本会非常高。
引自VUE中文官网的话:
“所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。”
那为修改props的时候,vue是如何能够知道是子组件修改并进行提示的呢?
/* 摘自vue.js源码 src/core/instance/state.js */
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)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
其核心奥秘就来自这段代码。
在组件进行initProps方法的时候,会执行defineReactive方法,这个方法就是运行Object.defineProperty对传入的object绑定get/set,传入的第四个参数是触发set的回调。所以props被修改时,就会查看是不是根组件、是不是更新子组件,那说明是子组件在修改props,给出warn警告。
但这里有一点,源码这里只是进行了浅度监听,即只能监听几种基础类型,如果Props是个对象,在子组件修改某个key的值的话就不会报错了。毕竟大家要按照约定去开发,所以作者意图应该只是简单做一下限制,防止一些新开发者误操作。