渲染
- 渲染需要走render函数
- 那么就需要找虚拟dom
babel编译
const obj = {
__self:null,
__source:null,
ref:null,
key:null
};
const REACT_ELEMENT = Symbol.for('react.typeof')
const ReactElement = (type,key,ref,props)=>{
return {
$$typeof: REACT_ELEMENT,
type,
key,
ref,
props
}
}
const jsxDev = (type, config)=>{
let ref=null,key=null,props={},propName;
if(config.ref){
ref=config.ref;
}
if(config.key){
key=config.key;
}
for(propName in config){
if(config.hasOwnProperty(propName)&&!obj.hasOwnProperty(propName)){
props[propName]=config[propName]
}
};
return ReactElement(type,key,ref,props);
}
- 我们会通过babel讲jsx转换成object的形式,那么函数执行的时候jsx函数就会将object变为虚拟dom,本质还是object
let element = <div>1</div>
编译后
let element = jsxDev('div',{child:1})
render函数介绍
- render会从实例中拿到刚才存储的FiberRoot
- 然后使用element来生成一次update,并放入根fiber的更新队列中
- 渲染时需要使用异步渲染操作,因为浏览器渲染与js执行互斥,如果单个任务占用时间过长会导致页面卡死
- 这时候需要有一个方法,在浏览器空闲的时候执行我们的更新,执行完成后将控制权给到浏览器,后续依次循环执行
- 屏幕刷新频率在60hz,那么1秒平分的话大约为16.6ms,所以留点时间判断
- requestIdleCallback,示例如下
const works = [1,2,3,4,5];
const sleep = (time)=>{
for(let o = Date.now();Date.now-o>time;){}
}
const run = ()=>{
sleep(works.shift());
}
const processWork = (deadline)=>{
if(deadline.timeRemaining>5&&works.length>0){
run()
}else if(works.length>0){
requestIdleCallback(processWork);
}
};
requestIdleCallback(processWork);
requestIdleCallback((deadline) => {
console.log('异步宏执行');
});
console.log('同步');
Promise.resolve().then(() => {
console.log('微任务');
})
渲染前需要根据vdom创建fiber树
- 先创建一个新的fiber空树,复制旧树的child、stateNode等属性,并且两者通过alternate关联
- 创建顺序:
- 以while循环遍历公共变量
- 先通过beginWork查找子节点,将子节点生成为fiber节点,然后继续向下寻找,找不到了就执行完成步骤
- 在完成步骤中,先将fiber变为真实dom,且将fiber的stateNode指向真实dom,然后把子节点的所有副作用累加到自身,并将子节点添加到自身
- 然后查找sibling,如果sibling不为null,将sibling给到beginWork,否则就继续拿到父级,依次向上走,最终找不到父为止,停止完成
let element = (<h1>
<div>
1
<span>2</span>
!
</div>
</h1>)
副作用
- 副作用为增删改查的标记(
不同的数值
),在创建fiber或更新删除的时候会给fiber添加
- 初次挂载的时候,只有根节点的child有一个添加副作用,其他都是全新的,没有标记
flags为自身的副作用
subtreeFlags为后代节点的副作用总和
提交阶段
- fiber和dom已经在内存中创完成了
- 这个时候需要将它添加到页面中去
- 我们刚才创建了一个新的根fiber,并且在上面进行更新和组装的,对应在根上的索引的就是
root.current.alternate
,把它给到root.finishedWork
上
- 然后开始处理,我们在dom的组装阶段累加了副作用,那么现在我们需要判断下,有副作用的就执行,如果没有副作用不需要处理
- 顺序:先按照子的副作用去处理,然后处理自身
- 在处理过程中,删除自身的副作用,然后插入到真实节点