Vue3中组件的渲染和批量更新原理-详细步骤

656 阅读3分钟

Vue3兼容了Vue2的写法,我们先用vue2的语法来写组件

image.png

在runtime-core模块下的vnode.ts文件里创建虚拟节点的createVnode方法里,第一步判断type类型的时候,我们修改原来的判断逻辑,增加上判断组件的逻辑

image.png

  • 先判断type是不是一个字符串,如果是一个字符串,那当前要创建的虚拟节点就是一个元素,否则判断type是不是一个对象,如果是一个对象,那么就是一个组件,否则就是0
  • STATEFUL_COMPONENT,这个就是我们平时写的组件标识,另一种就是函数式组件FUNCTIONAL_COMPONENT
  • 函数式组件在vue3中已经没什么用了,因为在vue3中这两种组件方式性能已经差不多一样快了

虚拟节点创建完后,开始renderer.ts里的渲染逻辑,就会走到patch方法,不清楚这个流程的看我之前的文章

在patch方法中,我们判断虚拟节点类型的时候也要加上判断组件的类型

image.png

我们来写processComponent函数

image.png

  • 当传过来的n1也就是老虚拟节点不存在,那么就走初始挂载流程

mountedComponent函数

image.png

  • 我们先拿到用户传过来的数据data和render渲染函数
  • 然后我们把data里的数据传入reactive,把data里的数据变成响应式的
  • pinia底层最核心的就是这句

组件上得有一些信息保存下来,我们需要创建一个实例来保存一些信息,vue2和vue3都是这样做的

image.png

  • state就是组件的状态
  • vnode是组件的虚拟节点
  • subTree是渲染的组件内容,也就是组件中render返回的虚拟节点
  • isMounted标识组件是否挂载
  • 最重要的就是这四个属性,后续其它属性我们再一点点加

我们再创建一个组件更新函数,然后创建一个响应式的Effect

image.png

  • 判断组件是否已经挂载,如果没有,则进行初始化
  • 最后创建一个响应式的Effect,把这个函数挂载到组件实例上,这样后续可以主动的重新渲染组件内,ReactiveEffect前面的文章实现过
  • 然后初始化执行一次,也就是初始化渲染一次

componentUpdateFn函数中初始化流程

image.png

  • 先拿到组件的内容虚拟节点
  • 调用patch,生成真实节点,挂载到容器中,patch方法第一个参数为null,会走创建流程
  • 然后我们把组件内容虚拟节点放到这个组件的实例上,最后更改实例上挂载标识

componentUpdateFn函数中组件更新流程

image.png

  • 拿到上一次的组件内容虚拟节点和当前组件的虚拟节点,调用patch走diff
  • patch方法第一个参数不为null,走更新逻辑

问题:组件的内容更新不能同步更新,不然你同步调用n次,会更新n次,所以我们要批量更新

image.png

  • 我们在创建响应式effect的时候,ReactiveEffect第二个参数传一个回调,用一个queneJob来进行处理,把组件的更新权run方法传进去
  • 当ReactiveEffect传了第二个参数,effect的执行权就不是自己内部执行了,而是会调用第二个参数回调。由这个回调函数来决定什么时候是更新,怎么更新。这个实际就是vue3中的调度器,我之前的文章有讲过它的原理

在runtime-core模块文件里创建scheduler.ts文件,创建queneJob方法

image.png

  • 创建一个队列quene
  • 创建一个变量来判断是否正在刷新
  • 函数中判断队列中如果没有当前任务,那么就把这个这个任务也就是组件的更新方法放入队列
  • 判断如果不是正在刷新,把开关打开,调用promise的then方法,在then中把开关关闭,然后循环队列执行队列中每一个任务
  • 至此,我们就实现了组件的批处理更新