现在我们只能向DOM树添加东西,那么更新和删除节点呢?we need to compare the elements we receive on the render function to the last fiber tree we committed to the DOM.
我们需要将在渲染函数上接收的元素与提交给DOM的最后一棵fiber树进行比较,所以我们需要在提交后保存上一个提交给DOM的fiber树的引用,我们叫他currentRoot
我们也给每个fiber添加alternate
属性alternate属性链接old fiber,该fiber是我们在之前提交阶段提交给DOM的节点
我们把peformUnitOfWork
中创建fiber的代码提取处理到reconcileChildren
中,该函数用于比较,然后进行添加节点、更新和删除
reconcileChildren
: 协调旧fiber和新元素
为了比较它们:
- 1、如果旧fiber和新元素有相同类型(更新它的属性)
effectTag: UPDATE
- 2、类型不同,且存在一个新元素(创建一个新fiber)
effectTag: PLACEMENT
- 3、类型不同,存在旧元素(删除旧元素)(我们使用一个deletions数组用于存放待删除的fiber旧节点)
effectTag: DELETION
(effectTag属性用于在提交阶段判断当前节点是什么状态(更新、新增、删除))
fiber节点处理完后,进入到提交(commit phase)阶段
提交之前先移除deletions里的节点
这边需要注意的是:更新节点,需要移除旧属性,然后设置新属性,还需要注意的是事件监听器属性,以on开头
首先新增currentRoot和alternate属性,创建一个deletions用于保存待删除fiber节点(并在render函数中初始化他)
1 | function commitRoot() { |
然后我们需要把performUnitOfWork中创建fiber的节点提取出来,在reconcileChildren中统一处理
1 | function performUnitOfWork(fiber) { |
此时fiber阶段已经处理完了,我们已经成功判断它到底是应该更新、删除、新增,并使用一个effectTag属性存储它的类型
fiber阶段处理完了,进入到提交阶段了
然后就应该在提交阶段根据effectTag属性进行处理
在提交之前,应该先移除deletions里面的节点
1 | function commitRoot() { |
现在我们已经能够删除、添加dom,但是updateDom函数还没有完成,所以我们应该处理以下它
此外我们需要一些额外的方法来帮助我们过滤属性,比如过滤children字段,过滤是否是事件监听器等
辅助函数如下
- 过滤非事件监听器(判断是否以on开头)
1 | const isEvent = key => key.startsWith('on'); |
- 用于过滤children与事件监听器
1 | const isProperty = key => key !== 'children' && !isEvent(key); |
过滤新旧属性相等的
1
const isNew = (prev, next) => key => prev[key] !== next[key];
过滤掉属性新属性的属性
1
const isGone = (prev, next) => key => !(key in next);
1 | //需要更新的dom,旧属性,新属性 |
这边还有一个地方需要修改的是,我们之前使用createDom建立dom节点时,只是把属性添加上去,并没有判断是否是事件监听器,所以,我们需要修改一下
1 | function createDom(fiber) { |