接上回说到,我们已经实现了h方法和createVnode方法,那么我们目前已经可以拿到处理后的虚拟dom和用户传入的renderOptions,接下来我就开始进行渲染
在runtime-core模块下的renderer.ts中我现在来着重写render方法
- 如果拿到vnode是空的话,说明是一个卸载逻辑
- 如果vnode不是空,那就是一个挂载逻辑
- 调用patch方法,容器上的_vode保存上次的vnode
patch方法
- 如果老的虚拟节点和新的虚拟节点一样的话,我们就直接return,什么都不用做
- 如果老的虚拟节点是null的话,说明此次渲染是初始渲染,调用mountElement方法进行挂载
- 结构renderOptions中对元素的一些操作
- mountElement中先生成元素,然后循环属性加到元素上
- 根据ShapeFlag进行与运算,判断儿子节点是否是文本节点,如果是,则将儿子节点创建文本节点插入。
- 根据ShapeFlag进行与运算,判断儿子是会否是数组,如果是数组,则调用mountChildren方法,递归循环创建
- 最后把整个元素放到容器中
- 如果老的虚拟节点不是null,走更新流程
问题1:我们上述的写法有一个问题,儿子节点是数组的情况下,这个数组里可能存在字符串而不是h处理过的虚拟节点
- 我们在vnode.ts中新添加一个类型
- 然后走到ptach方法初始化流程中我们判断一下类型,如果是一个Text类型,我们就针对这种文本调用processText进行处理
- 如果老的虚拟节点是null,则创建一个文本节点插入到容器中
- 在mountChildren中对递归的子节点进行判断,如果是个字符串,那么我们需要调用createVode创建一个文本类型的虚拟节点
卸载逻辑
- 如果新的虚拟节点为null并且之前渲染过,则走卸载流程
- 调用hostRemove进行卸载
总结:
- 用户调用h方法创建虚拟节点,然后传入render方法中
- 如果传入的新虚拟节点为null,走卸载流程
- 否则走patch方法,patch方法会先判断老虚拟节点和新虚拟节点是否相同,相同则不进行操作。然后根据容器上是否有_vode老虚拟节点来判断是初始渲染还是更新操作,如果为null,则走初始渲染流程
- 初始渲染流程中会先结构出renderOptions里对dom的操作,然后先生成真实元素挂载到虚拟节点的el属性上
- 之后遍历属性加到元素上,然后判断儿子节点的类型,儿子节点就两种情况,一个是文本节点,一个是数组,如果是文本节点,直接生成文本节点插入,否则进行递归调patch
- 最后把整个元素插入到容器中