Vue3兼容了Vue2的写法,我们先用vue2的语法来写组件
在runtime-core模块下的vnode.ts文件里创建虚拟节点的createVnode方法里,第一步判断type类型的时候,我们修改原来的判断逻辑,增加上判断组件的逻辑
- 先判断type是不是一个字符串,如果是一个字符串,那当前要创建的虚拟节点就是一个元素,否则判断type是不是一个对象,如果是一个对象,那么就是一个组件,否则就是0
- STATEFUL_COMPONENT,这个就是我们平时写的组件标识,另一种就是函数式组件FUNCTIONAL_COMPONENT
- 函数式组件在vue3中已经没什么用了,因为在vue3中这两种组件方式性能已经差不多一样快了
虚拟节点创建完后,开始renderer.ts里的渲染逻辑,就会走到patch方法,不清楚这个流程的看我之前的文章
在patch方法中,我们判断虚拟节点类型的时候也要加上判断组件的类型
我们来写processComponent函数
- 当传过来的n1也就是老虚拟节点不存在,那么就走初始挂载流程
mountedComponent函数
- 我们先拿到用户传过来的数据data和render渲染函数
- 然后我们把data里的数据传入reactive,把data里的数据变成响应式的
- pinia底层最核心的就是这句
组件上得有一些信息保存下来,我们需要创建一个实例来保存一些信息,vue2和vue3都是这样做的
- state就是组件的状态
- vnode是组件的虚拟节点
- subTree是渲染的组件内容,也就是组件中render返回的虚拟节点
- isMounted标识组件是否挂载
- 最重要的就是这四个属性,后续其它属性我们再一点点加
我们再创建一个组件更新函数,然后创建一个响应式的Effect
- 判断组件是否已经挂载,如果没有,则进行初始化
- 最后创建一个响应式的Effect,把这个函数挂载到组件实例上,这样后续可以主动的重新渲染组件内,ReactiveEffect前面的文章实现过
- 然后初始化执行一次,也就是初始化渲染一次
componentUpdateFn函数中初始化流程
- 先拿到组件的内容虚拟节点
- 调用patch,生成真实节点,挂载到容器中,patch方法第一个参数为null,会走创建流程
- 然后我们把组件内容虚拟节点放到这个组件的实例上,最后更改实例上挂载标识
componentUpdateFn函数中组件更新流程
- 拿到上一次的组件内容虚拟节点和当前组件的虚拟节点,调用patch走diff
- patch方法第一个参数不为null,走更新逻辑
问题:组件的内容更新不能同步更新,不然你同步调用n次,会更新n次,所以我们要批量更新
- 我们在创建响应式effect的时候,ReactiveEffect第二个参数传一个回调,用一个queneJob来进行处理,把组件的更新权run方法传进去
- 当ReactiveEffect传了第二个参数,effect的执行权就不是自己内部执行了,而是会调用第二个参数回调。由这个回调函数来决定什么时候是更新,怎么更新。这个实际就是vue3中的调度器,我之前的文章有讲过它的原理
在runtime-core模块文件里创建scheduler.ts文件,创建queneJob方法
- 创建一个队列quene
- 创建一个变量来判断是否正在刷新
- 函数中判断队列中如果没有当前任务,那么就把这个这个任务也就是组件的更新方法放入队列
- 判断如果不是正在刷新,把开关打开,调用promise的then方法,在then中把开关关闭,然后循环队列执行队列中每一个任务
- 至此,我们就实现了组件的批处理更新