原理深度解析Vue的响应式更新比React快

2020-06-16 06:51:34易采站长站整理

const child = vnode.componentInstance = oldVnode.componentInstance
updateChildComponent(
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children
)
},

在 updateChildComponent 内部


const hasChildren = !!(
// 这玩意就是 slot 元素
renderChildren || // has new static slots
vm.$options._renderChildren || // has old static slots
parentVnode.data.scopedSlots || // has new scoped slots
vm.$scopedSlots !== emptyObject // has old scoped slots
)

然后下面走一个判断


if (hasChildren) {
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
vm.$forceUpdate()
}

这里调用了 slot-comp 组件vm实例上的 $forceUpdate,那么它所触发的渲染watcher就是属于slot-comp的渲染watcher了。
总结来说,这次 msg 的更新不光触发了 parent-comp 的重渲染,也进一步的触发了拥有slot的子组件 slot-comp 的重渲染。
它也只是触发了两层渲染,如果 slot-comp 内部又渲染了其他组件 slot-child,那么此时它是不会进行递归更新的。(只要 slot-child 组件不要再有 slot 了)。

比起 React 的递归更新,是不是还是好上很多呢?

赠礼 一个小issue

有人给 Vue 2.4.2 版本提了一个issue,在下面的场景下会出现 bug。


let Child = {
name: "child",
template:
'<div><span>{{ localMsg }}</span><button @click="change">click</button></div>',
data: function() {
return {
localMsg: this.msg
};
},
props: {
msg: String
},
methods: {
change() {
this.$emit("update:msg", "world");
}
}
};

new Vue({
el: "#app",
template: '<child :msg.sync="msg"><child>',
beforeUpdate() {
alert("update twice");
},
data() {
return {
msg: "hello"
};
},
components: {
Child
}
});

具体的表现是点击 click按钮,会 alert 出两次 update twice。 这是由于子组件在执行 data 这个函数初始化组件的数据时,会错误的再收集一遍 Dep.target (也就是渲染watcher)。

由于数据初始化的时机是 beforeCreated -> created 之间,此时由于还没有进入子组件的渲染阶段, Dep.target 还是父组件的渲染watcher。

这就导致重复收集依赖,重复触发同样的更新

怎么解决的呢?很简单,在执行 data 函数的前后,把 Dep.target 先设置为 null 即可,在 finally 中再恢复,这样响应式数据就没办法收集到依赖了。