一个简易版的代码实现
schedule
import { TAG_ROOT, TAG_TEXT, TAG_HOST, ELEMENT_TEXT, PLACEMENT, DELETION, UPDATE, TAG_CLASS, TAG_FUNCTION_COMPONENT } from "./constant";
import { setProps } from './utils';
import { UpdateQueue, Update } from "./UpdateQueue";
/**
* 从根节点开始渲染和调度,
* 两个阶段:
* 1. diff阶段(render阶段):对比新旧的虚拟DOM,进行增量更新或者创建
* 根据虚拟DOM进行任务拆分。这个阶段可以中断。
* render阶段的任务: (1) 生成Fiber (2) 收集Effect List
* 2. commit阶段,进行DOM更新
*/
let nextUnitOfWork = null; // 下一个工作单元,
let workInProgressRoot = null; // RootFiber 应用的根 // 正在渲染的根Root Fiber
let currentRoot = null;
let deletions = []; // 删除的节点,我们并不放在effect list里面, 所以需要单独记录并且执行。
let workingProgressFiber = null; // 正在工作中的fiber
let hookIndex = 0; // hooks 索引
export function scheduleRoot(rootFiber) { // {tag: TAG_ROOT, stateNode:container, props:{children:[element]}}
if (currentRoot && currentRoot.alternate) { // 第二次之后的更新
workInProgressRoot = currentRoot.alternate; // 第一次渲染出来的那个fiber tree
workInProgressRoot.alternate = currentRoot; // 让这个树的替身指向当前的currentRoot;
if (rootFiber) {
workInProgressRoot.props = rootFiber.props; // 让它的props更新成新的props
}
} else if (currentRoot) { // 说明至少已经渲染过一次了
if (rootFiber) {
rootFiber.alternate = currentRoot;
workInProgressRoot = rootFiber; // 挂载后不变。
} else {
workInProgressRoot = {
...currentRoot,
alternate: currentRoot
}
}
} else { // 如果是第一次渲染
workInProgressRoot = rootFiber; // 挂载后不变。
}
workInProgressRoot.firstEffect = workInProgressRoot.lastEffect = workInProgressRoot.nextEffect = null;
nextUnitOfWork = workInProgressRoot; // 一直在变。
}
//
function performUnitOfWork(currentFiber) {
beginWork(currentFiber);
// 由于经过beginWork的调和,如果currentFiber的props.children有值,就会在currentFiber加上了child,指向第一个Fiber子元素。
if (currentFiber.child) {
return currentFiber.child;
}
// 如果不存在child, 那么这个任务就已经完成了,需要继续查看sibling,
while (currentFiber) {
completeUnitOfWork(currentFiber);
if (currentFiber.sibling) {
return currentFiber.sibling;
} else {
currentFiber = currentFiber.return; // 先找到父亲,然后让父亲完成。
}
}
}
// 在完成的时候需要收集有副作用的Fiber,然后组成effect list
// 每个fiber有两个属性,
// firstEffect指向第一个有副作用的子Fiber
// lastEffect指向最后一个有副作用的子Fiber
// 中间的用nextEffect做成一个单链表
function completeUnitOfWork(currentFiber) { // 第一个完成的A1TEXT
console.log('收集副作用:', currentFiber.tag, currentFiber.stateNode);
// 准备形成Effect List
let returnFiber = currentFiber.return; // A1
if (returnFiber) {
// 1.把自己儿子的effect链挂到父亲身上
if (!returnFiber.firstEffect) {
returnFiber.firstEffect = currentFiber.firstEffect;
}
if (currentFiber.lastEffect) {
if (returnFiber.lastEffect) {
// TODO: 这边为什么是这样的???
returnFiber.lastEffect.nextEffect = currentFiber.firstEffect;
}
returnFiber.lastEffect = currentFiber.lastEffect;
}
// 2. 把自己挂到父亲身上
const effectTag = currentFiber.effectTag;
if (effectTag) {// 自己有副作用
if (returnFiber.lastEffect) {
returnFiber.lastEffect.nextEffect = currentFiber;
} else {
returnFiber.firstEffect = currentFiber;
}
returnFiber.lastEffect = currentFiber;
}
}
}
/**
* beiginWork
* 1. 创建真实DOM元素
* 2. 创建子Fiber
*/
function beginWork(currentFiber) {
if (currentFiber.tag === TAG_ROOT) { // 根fiber
updateHostRoot(currentFiber);
} else if (currentFiber.tag === TAG_TEXT) { // 文本fiber
updateHostText(currentFiber);
} else if (currentFiber.tag === TAG_HOST) { // 原生DOM节点
updateHost(currentFiber);
} else if (currentFiber.tag === TAG_CLASS) { // 类组件
updateClassComponent(currentFiber);
} else if (currentFiber.tag === TAG_FUNCTION_COMPONENT) {
updateFunctionComponent(currentFiber);
}
}
/**
* 因为是根Fiber, 所以本身就存在真实DOM元素 #root;
* 直接开始遍历children,创建子Fiber;
*/
function updateHostRoot(currentFiber) {
let newChildren = currentFiber.props.children; // [element]
reconcileChildren(currentFiber, newChildren);
}
function updateHostText(currentFiber) {
// 如果此Fiber没有创建DOM节点,那么就需要创建
if (!currentFiber.stateNode) {
currentFiber.stateNode = createDOM(currentFiber);
}
}
function updateHost(currentFiber) {
if (!currentFiber.stateNode) {
currentFiber.stateNode = createDOM(currentFiber);
}
let newChildren = currentFiber.props.children; // [element]
reconcileChildren(currentFiber, newChildren);
}
function updateClassComponent(currentFiber) {
if (!currentFiber.stateNode) { // 类组件 stateNode 是组件的实例
// new ClassCounter();
// fiber和类组件实例 双向指向
currentFiber.stateNode = new currentFiber.type(currentFiber.props);
currentFiber.stateNode.internalFiber = currentFiber;
currentFiber.updateQueue = new UpdateQueue();
}
// 给组件的实例的state赋值
currentFiber.stateNode.state = currentFiber.updateQueue.forceUpdate(currentFiber.stateNode.state);
let newElement = currentFiber.stateNode.render();
const newChildren = [newElement];
reconcileChildren(currentFiber, newChildren);
}
function updateFunctionComponent(currentFiber) {
workingProgressFiber = currentFiber;
hookIndex = 0;
workingProgressFiber.hooks = [];
const newChildren = [currentFiber.type(currentFiber.props)]
reconcileChildren(currentFiber, newChildren);
}
function createDOM(currentFiber) {
if (currentFiber.tag === TAG_TEXT) {
return document.createTextNode(currentFiber.props.text);
} else if (currentFiber.tag === TAG_HOST) {
let stateNode = document.createElement(currentFiber.type);
updateDOM(stateNode, {}, currentFiber.props);
return stateNode;
}
}
function updateDOM(stateNode, oldProps, newProps) {
if (stateNode && stateNode.setAttribute) {
setProps(stateNode, oldProps, newProps);
}
}
// newChildren是一个虚拟DOM的数组, 把虚拟DOM转成Fiber
function reconcileChildren(currentFiber, newChildren) {
let newChildIndex = 0; // 新子节点的索引,
let prevSibling;
// 如果说currentFiber有alternate并且alternate有child属性;
let oldFiber = currentFiber.alternate && currentFiber.alternate.child;
if (oldFiber) {
oldFiber.firstEffect = oldFiber.lastEffect = oldFiber.nextEffect = null;
}
while (newChildIndex < newChildren.length || oldFiber) {
let newChild = newChildren[newChildIndex]; // 取出虚拟DOM节点。
let tag;
let newFiber; // 新的Fiber;
const sameType = oldFiber && newChild && oldFiber.type === newChild.type;
if (newChild && newChild.type === ELEMENT_TEXT) {
// 文本节点
tag = TAG_TEXT;
} else if (newChild && typeof newChild.type === 'string') {
//原生dom节点 div p span
tag = TAG_HOST;
} else if (newChild && typeof newChild.type === 'function' && newChild.type.prototype.isReactComponent) {
tag = TAG_CLASS;
} else if (newChild && typeof newChild.type === 'function') {
tag = TAG_FUNCTION_COMPONENT;
}
if (sameType) { // 说明老fiber和新虚拟DOM类型是一样的, 可以直接复用老的DOM节点,更新即可。
if (oldFiber.alternate) { // 说明至少已经更新一次了,复用,减少对象的创建。
// 上上棵树的tag,type等属性肯定是一样的, 因为alternate的赋值是在sameType中。
newFiber = oldFiber.alternate;
newFiber.props = newChild.props;
newFiber.alternate = oldFiber;
newFiber.effectTag = UPDATE;
newFiber.nextEffect = null;
newFiber.updateQueue = oldFiber.updateQueue || new UpdateQueue();
//
// newFiber.stateNode = oldFiber.stateNode; //
// newFiber.return = currentFiber; // 父Fiber,
} else {
newFiber = {
tag: oldFiber.tag,
type: oldFiber.type,
props: newChild.props,
stateNode: oldFiber.stateNode, //
return: currentFiber, // 父Fiber,
effectTag: UPDATE, // 副作用标识, render我们要收集副作用,
alternate: oldFiber,
nextEffect: null,
updateQueue: oldFiber.updateQueue || new UpdateQueue()
}
}
} else {
// 如果两两比较时候不一样,那就删除老的,添加新的。
if (newChild) {
newFiber = {
tag,
type: newChild.type,
props: newChild.props,
stateNode: null,
return: currentFiber, // 父Fiber,
effectTag: PLACEMENT, // 副作用标识, render我们要收集副作用,
nextEffect: null,
updateQueue: new UpdateQueue()
}
}
if (oldFiber) {
oldFiber.effectTag = DELETION;
deletions.push(oldFiber);
}
}
if (oldFiber) {
oldFiber = oldFiber.sibling;
}
if (newFiber) {
if (newChildIndex === 0) {
currentFiber.child = newFiber;
} else {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
}
newChildIndex++;
}
}
// 循环执行工作
function workLoop(deadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1; // 没有时间的话就要让出控制权。
}
if (!nextUnitOfWork && workInProgressRoot) {
console.log('render阶段结束');
console.log("最终形成的Fiber链:", workInProgressRoot);
commitRoot();
} else {
}
// 如果时间片到期后还有任务没有完成, 就需要请求浏览器再次调度。
requestIdleCallback(workLoop, { timeout: 500 });
}
function commitRoot() {
console.log('commitRoot', workInProgressRoot);
deletions.forEach(commitWork); // 执行effect list之前,先把该删除的元素删除
let currentFiber = workInProgressRoot.firstEffect;
while (currentFiber) {
commitWork(currentFiber);
currentFiber = currentFiber.nextEffect;
}
deletions.length = 0; // 提交之后要清空deletion数组
currentRoot = workInProgressRoot; // 把当前渲染成功的根Fiber赋给currentRoot;
workInProgressRoot = null;
}
function commitWork(currentFiber) {
if (!currentFiber) return;
let returnFiber = currentFiber.return;
while (
returnFiber.tag !== TAG_HOST &&
returnFiber.tag !== TAG_ROOT &&
returnFiber.tag !== TAG_TEXT
) {
returnFiber = returnFiber.return;
}
let returnDOM = returnFiber.stateNode;
if (currentFiber.effectTag === PLACEMENT) { // 新增节点
let nextFiber = currentFiber;
// 优化: 如果是类组件,其实可以直接return
if (nextFiber.tag === TAG_CLASS) {
return;
}
// 如果要挂载的节点不是DOM节点,比如说是类组件Fiber,一直找第一个儿子,直到找到一个真实DOM节点为止。
while (
nextFiber.tag !== TAG_HOST &&
nextFiber.tag !== TAG_ROOT &&
nextFiber.tag !== TAG_TEXT
) {
nextFiber = nextFiber.child;
}
returnDOM.appendChild(nextFiber.stateNode);
} else if (currentFiber.effectTag === DELETION) {// 删除节点
return commitDeletion(currentFiber, returnDOM);
// returnDOM.removeChild(currentFiber.stateNode);
} else if (currentFiber.effectTag === UPDATE) {
if (currentFiber.type === ELEMENT_TEXT) {
if (currentFiber.alternate.props.text != currentFiber.props.text) {
currentFiber.stateNode.textContent = currentFiber.props.text;
} else {
updateDOM(currentFiber.stateNode, currentFiber.alternate.props, currentFiber.props);
}
}
}
currentFiber.effectTag = null;
}
function commitDeletion(currentFiber, domReturn) {
if (currentFiber.tag == TAG_HOST || currentFiber.tag == TAG_TEXT) {
domReturn.removeChild(currentFiber.stateNode);
} else {
commitDeletion(currentFiber.child, domReturn);
}
}
/**
*
* workingProgressFiber = currentFiber;
* hookIndex = 0;
* workingProgressFiber.hooks = [];
*/
export function useReducer(reducer, initialValue) {
let newHook = workingProgressFiber.alternate && workingProgressFiber.alternate.hooks && workingProgressFiber.alternate.hooks[hookIndex];
if (newHook) {
console.log(hookIndex, newHook);
// 第二次渲染
newHook.state = newHook.updateQueue.forceUpdate(newHook.state);
} else {
newHook = {
state: initialValue,
updateQueue: new UpdateQueue()
}
}
// const dispatch = null;
const dispatch = action => { // {type: 'ADD'}
let payload = reducer ? reducer(newHook.state, action) : action;
newHook.updateQueue.enqueueUpdate(
new Update(payload)
);
scheduleRoot();
}
workingProgressFiber.hooks[hookIndex++] = newHook;
return [newHook.state, dispatch];
}
export function useState (initialValue){
return useReducer(null, initialValue);
}
requestIdleCallback(workLoop, { timeout: 500 });
UpdateQueue
export class Update {
constructor(payload) {
this.payload = payload
}
}
// 数据结构:是一个单链表
export class UpdateQueue {
constructor() {
this.firstUpdate = null;
this.lastUpdate = null;
}
enqueueUpdate(update) {
if (this.lastUpdate === null) {
this.firstUpdate = this.lastUpdate = update;
} else {
this.lastUpdate.nextUpdate = update;
this.lastUpdate = update;
}
}
forceUpdate(state) {
let currentUpdate = this.firstUpdate;
// TODO:
while(currentUpdate){
console.log(currentUpdate,'~~~~')
let nextState = typeof currentUpdate.payload === 'function' ? currentUpdate.payload(state) : currentUpdate.payload;
state = {...state, ...nextState};
currentUpdate = currentUpdate.nextUpdate;
}
this.firstUpdate = this.lastUpdate = null;
return state;
}
}
react-dom
import { TAG_ROOT } from "./constant";
import { scheduleRoot} from './schedule';
function render(element, container){
let rootFiber = {
tag:TAG_ROOT, // 每个Fiber会有一个tag标识此元素的类型
stateNode: container, // 指向真实的DOM元素
props: {
children: [element] // 放置需要渲染的React元素,即虚拟DOM元素。
}
}
scheduleRoot(rootFiber);
}
const ReactDOM = {
render
}
export default ReactDOM;
react
import { ELEMENT_TEXT } from "./constant";
import { UpdateQueue, Update} from './UpdateQueue';
import { scheduleRoot, useReducer, useState } from "./schedule";
/**
* 创建元素(虚拟DOM)
* @param {} type 元素的类型 div span p
* @param {*} config 配置对象 属性 key ref
* @param {...any} children 放着所有的儿子,做成一个数组。
*/
function createElement(type,config, ...children){
// 删除不必要的属性,简化代码
delete config.__self;
delete config.__source;
return {
type,
props: {
...config,
children:children.map(child => {
return typeof child === 'object'? child: {
type: ELEMENT_TEXT,
props: {
text: child,
children: []
}
}
})
}
}
}
class Component {
constructor(props){
this.props = props;
this.updateQueue = new UpdateQueue();
}
setState(payload){ //可能是对象,也可能是一个函数
let update = new Update(payload);
// updateQueue其实是放在此类组件对应的fiber节点的internalFiber;
this.internalFiber.updateQueue.enqueueUpdate(update);
// this.updateQueue.enqueueUpdate(update);
scheduleRoot(); // 从根节点开始调度;
}
}
Component.prototype.isReactComponent = {}; // 类组件
const React = {
createElement,
Component,
useReducer,
useState
}
export default React;