众所周知,vue中的props是单向数据流,如果在当前组件直接修改了props里面的值,非生产环境会出现如下警告:
vue.runtime.esm.js?c320:4573 [Vue 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: "xxx"
那么问题来了,vue中是如何实现单向数据流的检测,它是怎么知道当前数据的改动是由父组件引起的还是当前组件内部改动的。
通过查看报错的堆栈信息,我们可以发现当前警告信息是由这段代码抛出的:
defineReactive$$1(props, key, value, function () {
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
);
}
});
这句代码提示我们vue是根据isRoot和isUpdatingChildComponent这2个变量判断当前改动是从哪个组件发起的,通过变量查找我们分别可以找到这2个变量定义和使用的地方
// initProps 函数内,判断当前组件是否存在父组件
var isRoot = !vm.$parent;
// vuejs会初始化一个全局变量标志当前组件内的更新是否由父组件引起
var isUpdatingChildComponent = false;
通过分析上面2个变量,我们可以知道子组件直接改变props的值为什么会报错,但是为什么父组件直接改变传入子组件的props不会报错呢,答案就在下面这个函数内:
// updateChildComponent 更新子组件
// 当父组件prepatch的hook被触发时,会去调用updateChildComponent函数
function updateChildComponent (//参数略
) {
{
// 设置标志,声明当前改变由父组件引起
isUpdatingChildComponent = true;
}
// 更新子组件代码略
{
// 设置标志,声明当前改变由父组件引起结束
isUpdatingChildComponent = false;
}
}
思考:上面提到,如果在非生产环境vue会给出单向数据流不能被子组件修改的警告,但是这个数据会被修改吗?
其实答案就在报错提示中:
![](https://img-blog.csdnimg.cn/img_convert/7a4af5eab28121d5786f80757472390e.png)