Vue3更新操作的统一入口

169 阅读3分钟

引言:

上一篇我们实现了组件属性改变后,组件更新的原理。然而后续插槽改变也会引起组件的更新,这两块更新逻辑是不一样的,我们其实可以把这两块的逻辑最后触发更新的入口统一起来,在我们之前写到组件渲染原理的时候有讲到,组件其实也是基于effect的,effect的包装类上会有effect主动触发的方法,我们在组件初始渲染的时候会把这个方法挂到组件的实例上,那么我们在判断需要更新的时候,统一调实例的这个方法就可以了

在处理组件更新的方法updateComponent中

Image.png

  • 判断需不需要更新,如果需要更新,调用实例上的update方法,实际上就是调用effect的包装类上的run方法
  • 保留新的虚拟节点

shouldUpdateComponent方法

Image.png

  • 如果老的虚拟节点上外部传的属性没有变化,那么我们就不更新,否则走上篇我们写到的具体比较属性是否变化的方法,比较个数和比较值,两个维度去比较
  • 判断前后儿子是否有值,就值就更新,这其实是插槽的更新判断

在处理组件内部更新的逻辑中

Image.png

  • 拿到刚才我们在实例挂载的新的虚拟节点
  • 更新前拿到新的属性来进行更新
  • 注意首次属性更新触发这个effect的时候,实例上是没有next这个属性的,然后更新调用patch,经过判断需要不要更新,需要更新主动调effect,又会走到这里,这个时候是有next属性的,然后进行简单的属性覆盖。

updateComponentPreRender方法

Image.png

  • 拿老的属性和新的属性进行比较

updateProps方法

Image.png

  • 逻辑能走到这个方法,那肯定是需要更新的,我们就直接覆盖属性,并删除多余的属性

总结

  • 组件更新的时候,我们就看一下组件需不需要更新,属性不一样,有插槽,需要更新,属性的值不一样也要进行更新,无论怎么更新,只要判断需要更新,我就调一下实例上的update方法来更新,同时我们还要把新的虚拟节点存到组件实例上,不然一会组件内容更新我们无法知道更新成什么
  • 在更新的时候,我们把新的虚拟节点取出来,去更新组件的虚拟节点为最新的,并且把刚才用于存最新的虚拟节点的实例上的变量清空,这个变量是临时的
  • 最后去调属性更新的方法,把属性更新调,那可能这里有人会疑惑,我去改变属性,那不是还会触发更新么,其实vue3的内部会判断,如果当前运行的组件和要更新的组件是同一个,会屏蔽掉,所以我们这里只是一个简单的赋值而已
  • 属性更新后,会继续走patch进行组件的重新渲染,把老的组件内容和新的组件内容传进去
  • 后面所有的更新逻辑,我们最后只需要调实例上的update方法,然后触发组件的effect,里面会走组件更新逻辑,在这里面最后调patch重新渲染之前,我们最新的组件内容做一些特殊处理,比如更改一些属性啊、赋值啊等等
  • 至此我们就统一了更新的入口,不然靠一些响应式的属性更新触发effect等会比较弱,逻辑也会四散开来,难以维护。所以Vue3很巧妙的把更新入口统一起来。