第一天
实现最简mini-react
目标:在页面中呈现hi-mini-react
字符,api和react保持一致。
采用渐进式
- 先从简单的入手,慢慢完善丰满。
v0.1
直接用利用dom api,在根结点中插入文本节点。
v0.2
变得可复用
用js对象描述节点,也就是vdom。
根据vdom用dom api创建元素。
使用工厂函数创建vdom。
写一个render函数,将vdom转换成真实dom,并挂载到页面上。
修改成类似react的api风格
jsx
问题:如何使用jsx?
目标:使用jsx
利用vite实现jsx的解析
jsx最终会转换成React.createElement
注意要提前导入React
第二天
实现任务调度器
问题
dom树特别大,导致渲染卡顿
js是单线程的,进行大量计算的时候,会阻塞浏览器渲染页面
解决思路
把大任务拆分到多个task里面完成
采用分治思想,把大任务拆分成多个小任务,让小任务在空闲时间执行,这样可以避免一次性进行大量计算导致阻塞。
实现
采用requestIdleCallback
分帧运算
requestIdleCallback
api可以实现。
插入一个函数,在空闲时间执行。
函数执行时,会接收一个参数,IdleDeadline
,可以拿到还剩多少空闲时间,如果空闲时间还够的话,就接着执行任务,直到空闲时间小于1。
新的问题,如何拆分任务,控制渲染,又如何恢复?
实现fiber架构
问题
- 如何做到每次只渲染几个节点?
- 在下次执行的时候依然从之前的位置执行?
解决思路
一次只渲染一个节点,依次渲染。
采用链表的方式把任务串起来。
把dom树转换 成链表结构。
- child
- sibling
- uncle
使用任务调度器,按照顺序渲染节点。
两种方式:
- 执行之前,把树转换成链表
- 边转换边调度(更优)
渲染当前节点A
时要做的事:
- 生成真实dom,赋上对应属性
- 转换成链表
- 将
A
相关信息都初始化。
A
的子节点是什么,子节点的下一个兄弟节点是什么。 - 渲染结束时返回下一个节点。
第三天
实现统一提交
问题
中途有可能没空余时间,用户会看到渲染一半的dom。
解决思路
计算结束后,将dom统一提交。
支持function component
问题
如何支持function component
解决思路
把fc当成一个盒子
- 开箱
目前createDom
无法解析type是function的节点。
由于函数的返回值vdom,是可以解析的。
所以我们要调用函数拿到vdom,可以看作是一个开箱的过程。把拿到的vdom作为函数组件节点的children
。
要注意,函数组件节点本身是没有dom的,所以在统一提交阶段要特殊处理。
- props
开箱的时候,把props传入进去。
第四天
复习前面所学。
第五天
实现事件绑定
在jsx中绑定一个事件,其实就是传递一个属性,在props
中。
事件的标志就是on
开头,在处理属性的时候绑定事件即可。
实现更新
更新时需要对比新旧fiber,在构建新fiber链表时,用alternate
属性关联旧fiber,effectTag
标识新建还是更新。在操作dom阶段,比较新旧props,根据fiber上的effectTag
区分更新还是挂载。
第六天
实现更新中的创建和删除
- type不一致,删除旧的,创建新的。
把要删除的节点收集起来。在commitWork
时,统一处理删除逻辑。
对于函数组件的删除要特殊处理,因为函数组件的fiber.dom
为null
。 - 新的比老的短,多出来的节点需要删除掉。
老的长时,新的遍历先结束,所以要把老的剩余部分遍历收集起来,删掉。 - 根据case不断完善。
- 解决edgecase
- 画图分析
- debug
优化更新逻辑
问题
更新子组件,不相关组件也会重新执行,浪费性能。
解决思路
之前每次更新都是从根节点开始,导致要遍历整棵树。
- 开始节点
触发更新的那个节点。 - 结束节点
在进行到它的兄弟节点时停止。
第七天
实现useState
- 在一个
fc
中,state
按照顺序存在数组中。 - 将状态数组存在
fiber
上 - 每次执行fc时,拿到旧fiber上的状态数组,按照顺序读取状态,并存在新的fiber上。
setState
每次都是重新创建的,由于闭包的原因,state也是最新的。- 每次调用setState后,先收集到
queue
中,最后统一执行更改state操作,优化视图渲染次数。 - state不变时不更新
第八天
实现useEffect
- 把
effectHooks
存在fiber
上。 - 每一个
effectHook
会有callback
、deps
和cleanup
属性。 - 在dom挂载后,遍历fiber,依次执行所有cleanup,在依次执行callback。
学习心得
确定目标,小步骤开发,一次只专注一件事情,先实现功能,再重构优化。
遇到问题先画图或debug理清思路,找到问题的原因再尝试解决。