Fiber
开发环境配置
目录介绍
文件/文件夹 | 描述 |
---|---|
src | 源代码 |
dist | 存储客户端打包文件 |
build | 存储服务端打包文件 |
server.js | 服务端代码 |
webpack.config.server.js | 服务端 webpack 配置文件 |
webpack.config.client.js | 客户端 webpack 配置文件 |
babel.config.js | bable 配置文件 |
项目依赖
npm init -y
yarn add webpack webpack-cli webpack-node-externals @babel/core @babel/preset-env @babel/preset-react babel-core babel-loader nodemon npm-run-all -D
yarn add express -D
服务端打包配置
.babel.cofig.js
module.exports = {
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
server.js
import express from "express";
const app = express();
const template = `
<html>
<head>
<title>React Fiber</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
`
app.get("*", (req, res) => {
res.send(template);
});
app.listen(4000, () => console.log('server is running.'));
webpack.config.server.js
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node',
mode: 'development',
entry: './server.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'server.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
},
externals: [ nodeExternals() ]
}
package.json
{
"name": "fiber",
"version": "1.0.0",
"description": "",
"main": "babel.config.js",
"scripts": {
"dev:serve-compile": "webpack --config webpack.config.server.js --watch",
"dev:serve": "nodemon ./build/server.js",
"start": "npm-run-all --parallel dev:*"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.2",
"express": "^4.17.1",
"nodemon": "^2.0.12",
"npm-run-all": "^4.1.5",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0",
"webpack-node-externals": "^3.0.0"
}
}
执行 yarn start 就可以启动本地开发服务器。
客户端打包配置
src/index.js
console.log('index');
server.js
import express from "express";
const app = express();
app.use(express.static('dist'));
const template = `
<html>
<head>
<title>React Fiber</title>
</head>
<body>
<div id="root"></div>
<script src="build.js"></script>
</body>
</html>
`
app.get("*", (req, res) => {
res.send(template);
});
app.listen(4000, () => console.log('server is running.'));
webpack.config.client.js
const path = require('path');
module.exports = {
target: 'web',
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'build.js'
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
}
package.json
{
"name": "fiber",
"version": "1.0.0",
"description": "",
"main": "babel.config.js",
"scripts": {
"dev:client-compile": "webpack --config webpack.config.client.js --watch",
"dev:server-compile": "webpack --config webpack.config.server.js --watch",
"dev:server": "nodemon ./build/server.js",
"start": "npm-run-all --parallel dev:*"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.2",
"express": "^4.17.1",
"nodemon": "^2.0.12",
"npm-run-all": "^4.1.5",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0",
"webpack-node-externals": "^3.0.0"
}
}
requestIdleCallback API
react fiber 中使用的核心 API。
功能介绍
利用浏览器的空余时间执行任务,如果存在更高优先级的任务要执行时,当前的任务可以被中止,优先执行高级别任务。
requestIdleCallback (function (deadline) {
// deadline.timeRemaining() // 获取浏览器空余时间
})
浏览器空余时间
页面是一帧一帧绘制出来的,当每秒绘制的帧数达到 60 时,页面是流畅的,小于这个值时,用户会感觉卡顿。
1s 60 帧,每一帧分到的时间分别是 1000 / 60 = 16 ms,如果每一帧执行的时间小于 16 ms,就说明浏览器有空余时间。如果任务在剩余的时间内没有完成则会停止任务执行,继续优先执行主任务,也就是说 requestIdleCallback 总是会利用浏览器的空余时间执行任务。
代码案例
页面中存在两个按钮和一个 DIV,点击第一个按钮执行一项复杂的计算,使其长期占用主线程,当计算任务执行的时候去点击第二个按钮更改页面中 DIV 的背景颜色。使用 requestIdleCallback 就可以完美解决这个卡顿问题。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
padding: 20px;
background-color: palegoldenrod;
}
</style>
</head>
<body>
<div id="box"></div>
<button id="btn1">执行计算任务</button>
<button id="btn2">更改背景颜色</button>
<script>
const oBox = document.getElementById('box');
const oBtn1 = document.getElementById('btn1'),
oBtn2 = document.getElementById('btn2');
let number = 99999;
let value = 0;
function calc (deadline) {
while (number >= 0 && deadline.timeRemaining() > 1) {
value = Math.random() < 0.5 ? Math.random() : Math.random();
number--;
console.log(value);
}
requestIdleCallback(calc);
}
oBtn1.onclick = function () {
requestIdleCallback(calc);
};
oBtn2.onclick = function () {
oBox.style.background = 'green';
}
</script>
</body>
</html>
Satck 算法问题、Fiber 解决方案
存在问题
React 16 的 Fiber 是 DOM 比对的一种新的算法,Fiber 就是这种算法的名字。之前的 DOM 比对算法叫做 Stack 。
React 16 之前的版本比对更新 Virtual DOM 的过程采用循环加递归实现。这种比对方式有一个问题,就是一旦任务开始进行就无法中断,如果应用中组件数量庞大、主线程被长期占用,知道整棵 Virtual DOM 树比对更新完成之后主线程才能释放,主线程才能执行其他任务。这就会导致一些用户交互、动画等任务无法立即得到执行,页面就会产生卡顿,非常的影响用户体验。
因为递归使用的是 JavaScript 自身的执行栈,所以旧版 Virtual DOM 比对的算法叫做 Stack,Stack 就是堆栈的意思。
核心问题:递归无法中断,执行重任务耗时长。JavaScript 又是单线程,无法同时执行其他任务,导致任务延迟页面卡顿,用户体验差。
解决方案
React 16 版本的解决方案。
- 利用浏览器空闲时间执行任务,拒绝长时间占用主线程;
- 采用 requestIdleCallback API,利用浏览器空闲时间进行 DOM 比对过程,不会长期占用主线程;
- 放弃递归只采用循环,因为循环可以被中断;
- 递归不可被打断,循环可以被中止,只要将循环的条件保存下来,下一次任务就可以从中断的时候执行;
- 任务拆分,将任务拆分成一个个的小任务。
- 如果任务想要实现中止再继续,任务的单元必须要小,这样即使任务没有执行完就被中止,重新执行任务的代价会小很多;
- 之前将整棵 virtual dom 的比对看作是一个任务,现在我们可以将树中每个节点的比对看作是一个任务;
- Fiber 的意思就是纤维,意思就是执行任务的颗粒度变的更加细腻;
核心就是第一点,第二点和第三点都是为了实现第一种而存在的。
实现思路
Fiber 方案中,为了实现任务的终止再继续,DOM 比对算法被分成两部分:Virtual DOM 的比对和真实 DOM 的更新。
Virtual DOM 的比对是可以被中止的,真实 DOM 的更新不可被中止。
使用 React 编写用户界面时,仍然使用 JSX 语法,babel 会将 JSX 语法转换为 React.createElement 的调用,React.createElement 方法在调用后会返回 Virtual DOM 对象,接下来就可以执行第一个阶段。
- 构建 Fiber 对象(可中断);
- 采用循环的方式从 Virtual DOM 对象中找到每一个内部的 Virtual DOM 对象,为每一个 Virtual DOM 对象构建 Fiber 对象;
- Fiber 对象也是 JavaScript 对象,它是从 Virtual DOM 对象演化来的,Fiber 对象中除了存在 type、prop、children 属性之外,还存在更多关于节点的信息,比如存在当前节点需要执行的操作(删除、更新、新增、修改等);
- 当所有的 Fiber 对象构建完成之后,需要将所有的 Fiber 对象存储在一个数组中,接下来就可以执行第二阶段操作。
- 提交 Commit (不可中断);
- 循环 Fiber 数组,根据 Fiber 对象中当前节点需要执行的任务类型,将这个操作应用在真实 DOM 对象中。
DOM 初始渲染:virtual DOM -> Fiber -> Fiber[] -> DOM
DOM 更新操作:newFiber vs oldFiber -> Fiber[] -> DOM
Fiber 对象
{
type 节点类型(元素、文本、组件)
props 节点属性
stateNode 节点 DOM 对象 | 组件实例对象
tag 节点标记(对具体类型的分类 hostRoot || hostComponent || classComponent || functionComponent)
effects 数组,存储需要更改的 fiber 对象
effectTag 当前 Fiber 要被执行的操作(新增、删除、修改)
parent 当前 Fiber 的父级 Fiber
child 当前 Fiber 的子级 Fiber
sibling 当前 Fiber 的下一个兄弟 Fiber
alternate Fiber 备份 fiber 比对时使用
}
创建任务队列并添加任务
react/misc/createTaskQueue/index.js
export default function createTaskQueue () {
const taskQueue = [];
return {
push: item => taskQueue.push(item),
pop: () => taskQueue.shift(),
};
}
react/misc/index.js
export { default as createTaskQueue } from './createTaskQueue';
react/reconciliation/index.js
import { createTaskQueue } from "../misc"
const taskQueue = createTaskQueue();
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
}
react/index.js
import createElement from "./createElement";
export { render } from './reconciliation';
export default {
createElement
}
index.js
import React, { render } from './react';
const jsx = (
<div>
<p>Hello React</p>
</div>
);
render(jsx, document.getElementById('root'));
实现任务的调度逻辑
react/reconciliation/index.js
import { createTaskQueue } from "../misc"
const taskQueue = createTaskQueue();
let subTask = null;
const getFirstTask = () => {
}
const executeTask = fiber => {
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
构建节点 Fiber 对象
react/misc/arrified/index.js
const arrified = arg => Array.isArray(arg) ? arg : [arg];
export default arrified;
react/reconciliation/index.js
import { createTaskQueue, arrified } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: 'host_component',
effects: [],
effectTag: 'placement',
stateNode: null,
parent: fiber
}
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
reconcileChildren(fiber, fiber.props.children);
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
完善 fiber 对象:tag、stateNode
react/dom/createDOMElement.js
import updateNodeElement from "./updateNodeElement";
export default function createDOMElement (virtualDOM) {
let newElement = null;
if (virtualDOM.type === 'text') {
// 文本节点
newElement = document.createTextNode(virtualDOM.props.textContent);
} else {
// 元素节点
newElement = document.createElement(virtualDOM.type);
// 更新元素属性
updateNodeElement(newElement, virtualDOM);
}
return newElement;
}
react/dom/updateNodeElement.js
export default function updateNodeElement (newElement, virtualDOM, oldVirtualDOM) {
// 获取节点对应的属性对象
const newProps = virtualDOM.props || {};
// 获取旧节点对应的属性对象
const oldProps = oldVirtualDOM && oldVirtualDOM.props || {};
Object.keys(newProps).forEach(propName => {
// 获取新的属性值
const newPropsValue = newProps[propName];
// 获取旧的属性值
const oldPropsValue = oldProps[propName];
if (newPropsValue !== oldPropsValue) {
// 判断属性是否事件属性 onClick
if (propName.slice(0, 2) === 'on') {
// 事件名称
const eventName = propName.toLowerCase().slice(2);
// 为元素添加事件
newElement.addEventListener(eventName, newPropsValue);
// 删除原有的事件处理函数
if (oldPropsValue) {
newElement.removeEventListener(eventName, oldPropsValue);
}
} else if (propName === 'value' || propName === 'checked') {
newElement[propName] = newPropsValue;
} else if (propName !== 'children') {
if (propName === 'classname') {
newElement.setAttribute('class', newPropsValue);
} else {
newElement.setAttribute(propName, newPropsValue);
}
}
}
});
// 判断属性被删除的情况
Object.keys(oldProps).forEach(propName => {
const newPropsValue = newProps[propName];
const oldPropsValue = oldProps[propName];
// 原有属性被删除
if (!newPropsValue) {
if (propName.slice(0, 2) === 'on') {
const eventName = propName.toLowerCase().slice(2);
newElement.removeListener(eventName, oldPropsValue);
} else if (propName !== 'children') {
newElement.removeAttribute(propName);
}
}
});
}
react/dom/index.js
export { default as createDOMElement } from './createDOMElement';
export { default as updateNodeElement } from './updateNodeElement';
react/misc/arrified/index.js
export default function arrified (arg) {
return Array.isArray(arg) ? arg : [arg];
}
react/misc/createStateNode/index.js
import { createDOMElement } from '../../dom';
export default function createStateNode (fiber) {
if (fiber.tag === 'host_component') {
return createDOMElement(fiber);
}
}
react/misc/getTag/index.js
export default function getTag (vdom) {
if (typeof vdom.type === 'string') {
return 'host_component';
}
}
react/reconciliation/index.js
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
newFiber.stateNode = createStateNode(newFiber);
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
reconcileChildren(fiber, fiber.props.children);
console.log(fiber);
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
构建节点树剩余子节点 Fiber 对象
src/index.js
import React, { render } from './react';
const jsx = (
<div>
<p>Hello React</p>
<p>Hello Fiber</p>
</div>
);
render(jsx, document.getElementById('root'));
src/react/reconciliation/index.js
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
newFiber.stateNode = createStateNode(newFiber);
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
reconcileChildren(fiber, fiber.props.children);
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
console.log(fiber);
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
构建 effect 数组
src/react/reconciliation/index.js
const executeTask = fiber => {
reconcileChildren(fiber, fiber.props.children);
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
console.log(fiber);
}
实现 fiber 初始渲染
src/react/reconciliation/index.js
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
fiber.effects.forEach(item => {
if (item.effectTag === 'placement') {
item.parent.stateNode.appendChild(item.stateNode);
}
});
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
newFiber.stateNode = createStateNode(newFiber);
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
reconcileChildren(fiber, fiber.props.children);
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
类组件处理
src/react/component/index.js
export class Component {
constructor (props) {
this.props = props;
}
}
src/react/misc/createReactInstance.js
export function createReactInstance (fiber) {
let instance = null;
if (fiber.tag === 'class_component') {
instance = new fiber.type(fiber.props);
} else {
instance = fiber.type;
}
return instance;
}
src/react/misc/createStateNode.js
import { createDOMElement } from '../../dom';
import { createReactInstance } from '../createReactInstance';
export default function createStateNode (fiber) {
if (fiber.tag === 'host_component') {
return createDOMElement(fiber);
}
return createReactInstance(fiber);
}
src/react/misc/getTag.js
import { Component } from "../../component";
export default function getTag (vdom) {
if (typeof vdom.type === 'string') {
return 'host_component';
}
if (Object.getPrototypeOf(vdom.type) === Component) {
return 'class_component';
}
return 'function_component';
}
src/react/reconciliation/index.js
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
fiber.effects.forEach(item => {
if (item.effectTag === 'placement') {
let fiber = item;
let parentFiber = item.parent;
while (parentFiber.tag === 'class_component') {
parentFiber = parentFiber.parent;
}
if (fiber.tag === 'host_component') {
parentFiber.stateNode.appendChild(fiber.stateNode);
}
}
});
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
newFiber.stateNode = createStateNode(newFiber);
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
if (fiber.tag === 'class_component') {
reconcileChildren(fiber, fiber.stateNode.render());
} else {
reconcileChildren(fiber, fiber.props.children);
}
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
src/react/index.js
import createElement from "./createElement";
export { render } from './reconciliation';
export { Component } from './component';
export default {
createElement
}
src/index.js
import React, { render, Component } from './react';
class Greating extends Component {
constructor (props) {
super(props);
}
render () {
return (
<div>Hello React Component</div>
)
}
};
render(<Greating />, document.getElementById('root'));
处理函数组件
src/index.js
import React, { render, Component } from './react';
function FnComponent (props) {
return (
<div>Hello React Component:{ props.title }</div>
)
}
render(<FnComponent title="Hello" />, document.getElementById('root'));
src/react/reconciliation/index.js
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
fiber.effects.forEach(item => {
if (item.effectTag === 'placement') {
let fiber = item;
let parentFiber = item.parent;
while (parentFiber.tag === 'class_component' || parentFiber.tag === 'function_component') {
parentFiber = parentFiber.parent;
}
if (fiber.tag === 'host_component') {
parentFiber.stateNode.appendChild(fiber.stateNode);
}
}
});
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null;
let prevFiber = null;
while (index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
newFiber.stateNode = createStateNode(newFiber);
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
if (fiber.tag === 'class_component') {
reconcileChildren(fiber, fiber.stateNode.render());
} else if (fiber.tag === 'function_component') {
reconcileChildren(fiber, fiber.stateNode(fiber.props));
} else {
reconcileChildren(fiber, fiber.props.children);
}
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
实现节点更新
src/index.js
import React, { render, Component } from './react';
const jsx = (
<div>
<p>Hello React</p>
<p>Hello Fiber</p>
</div>
);
render(jsx, document.getElementById('root'));
setTimeout(() => {
const newJsx = (
<div>
<p>666</p>
<p>Hello Fiber</p>
</div>
);
render(newJsx, document.getElementById('root'));
}, 2000)
src/react/reconciliation/index.js
import { updateNodeElement } from "../dom";
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
// 循环 effects 数组,构建 DOM 节点树
fiber.effects.forEach(item => {
if (item.effectTag === 'update') {
if (item.type === item.alternate.type) {
// 节点类型相同
updateNodeElement(item.stateNode, item, item.alternate);
} else {
// 节点类型不同
item.parent.stateNode.replaceChild(
item.stateNode,
item.alternate.stateNode
)
}
} else if (item.effectTag === 'placement') {
let fiber = item;
let parentFiber = item.parent;
while (parentFiber.tag === 'class_component' || parentFiber.tag === 'function_component') {
parentFiber = parentFiber.parent;
}
if (fiber.tag === 'host_component') {
parentFiber.stateNode.appendChild(fiber.stateNode);
}
}
});
// 备份旧根 fiber 对象
fiber.stateNode.__rootFiberContainer = fiber;
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null,
alternate: task.dom.__rootFiberContainer
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null; // 子节点的 vritualDOM 对象
let newFiber = null; // 子级 fiber 对象
let prevFiber = null; // 上一个兄弟 fiber 对象
let alternate = null;
if (fiber.alternate && fiber.alternate.child) {
alternate = fiber.alternate.child;
}
while (index < numberOfElements) {
// 子级 Virtual DOM 对象
element = arrifiedChildren[index];
if (element && alternate) {
// 更新操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'update',
parent: fiber,
alternate
}
if (element.type === alternate.type) {
// 类型相同
newFiber.stateNode = alternate.stateNode;
} else {
// 类型不同
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
}
} else if (element && !alternate) {
// 初始渲染操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
} else {
}
if (index === 0) {
fiber.child = newFiber;
} else {
prevFiber.siblint = newFiber;
}
if (alternate && alternate.siblint) {
alternate = alternate.siblint;
} else {
alternate = null;
}
// 更新
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
if (fiber.tag === 'class_component') {
reconcileChildren(fiber, fiber.stateNode.render());
} else if (fiber.tag === 'function_component') {
reconcileChildren(fiber, fiber.stateNode(fiber.props));
} else {
reconcileChildren(fiber, fiber.props.children);
}
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
扩展更新节点方法
原有 updateNodeElement 方法不支持更新文本节点。
const jsx = (
<div>
<p>Hello React</p>
<p>Hello Fiber</p>
</div>
);
render(jsx, document.getElementById('root'));
setTimeout(() => {
const newJsx = (
<div>
<div>666</div>
<p>Hello Fiber</p>
</div>
);
render(newJsx, document.getElementById('root'));
}, 2000)
export default function updateNodeElement (newElement, virtualDOM, oldVirtualDOM) {
// 获取节点对应的属性对象
const newProps = virtualDOM.props || {};
// 获取旧节点对应的属性对象
const oldProps = oldVirtualDOM && oldVirtualDOM.props || {};
if (virtualDOM.type === 'text') {
if (newProps.textContent !== oldProps.textContent) {
if (virtualDOM.parent.type !== oldVirtualDOM.parent.type) {
virtualDOM.parent.stateNode.appendChild(
document.createTextNode(newProps.textContent)
);
} else {
virtualDOM.parent.stateNode.replaceChild(
document.createTextNode(newProps.textContent),
oldVirtualDOM.stateNode
);
}
}
return;
}
Object.keys(newProps).forEach(propName => {
// 获取新的属性值
const newPropsValue = newProps[propName];
// 获取旧的属性值
const oldPropsValue = oldProps[propName];
if (newPropsValue !== oldPropsValue) {
// 判断属性是否事件属性 onClick
if (propName.slice(0, 2) === 'on') {
// 事件名称
const eventName = propName.toLowerCase().slice(2);
// 为元素添加事件
newElement.addEventListener(eventName, newPropsValue);
// 删除原有的事件处理函数
if (oldPropsValue) {
newElement.removeEventListener(eventName, oldPropsValue);
}
} else if (propName === 'value' || propName === 'checked') {
newElement[propName] = newPropsValue;
} else if (propName !== 'children') {
if (propName === 'classname') {
newElement.setAttribute('class', newPropsValue);
} else {
newElement.setAttribute(propName, newPropsValue);
}
}
}
});
// 判断属性被删除的情况
Object.keys(oldProps).forEach(propName => {
const newPropsValue = newProps[propName];
const oldPropsValue = oldProps[propName];
// 原有属性被删除
if (!newPropsValue) {
if (propName.slice(0, 2) === 'on') {
const eventName = propName.toLowerCase().slice(2);
newElement.removeListener(eventName, oldPropsValue);
} else if (propName !== 'children') {
newElement.removeAttribute(propName);
}
}
});
}
实现节点删除操作
const jsx = (
<div>
<p>Hello React</p>
<p>Hello Fiber</p>
</div>
);
render(jsx, document.getElementById('root'));
setTimeout(() => {
const newJsx = (
<div>
<p>Hello Fiber</p>
</div>
);
render(newJsx, document.getElementById('root'));
}, 2000)
import { updateNodeElement } from "../dom";
import { createTaskQueue, arrified, createStateNode, getTag } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
// 循环 effects 数组,构建 DOM 节点树
fiber.effects.forEach(item => {
if (item.effectTag === 'delete') {
item.parent.stateNode.removeChild(item.stateNode);
} else if (item.effectTag === 'update') {
if (item.type === item.alternate.type) {
// 节点类型相同
updateNodeElement(item.stateNode, item, item.alternate);
} else {
// 节点类型不同
item.parent.stateNode.replaceChild(
item.stateNode,
item.alternate.stateNode
)
}
} else if (item.effectTag === 'placement') {
let fiber = item;
let parentFiber = item.parent;
while (parentFiber.tag === 'class_component' || parentFiber.tag === 'function_component') {
parentFiber = parentFiber.parent;
}
if (fiber.tag === 'host_component') {
parentFiber.stateNode.appendChild(fiber.stateNode);
}
}
});
// 备份旧根 fiber 对象
fiber.stateNode.__rootFiberContainer = fiber;
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null,
alternate: task.dom.__rootFiberContainer
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null; // 子节点的 vritualDOM 对象
let newFiber = null; // 子级 fiber 对象
let prevFiber = null; // 上一个兄弟 fiber 对象
let alternate = null;
if (fiber.alternate && fiber.alternate.child) {
alternate = fiber.alternate.child;
}
while (index < numberOfElements || alternate) {
// 子级 Virtual DOM 对象
element = arrifiedChildren[index];
if (!element && alternate) {
// 删除操作
alternate.effectTag = 'delete';
fiber.effects.push(alternate);
} else if (element && alternate) {
// 更新操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'update',
parent: fiber,
alternate
}
if (element.type === alternate.type) {
// 类型相同
newFiber.stateNode = alternate.stateNode;
} else {
// 类型不同
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
}
} else if (element && !alternate) {
// 初始渲染操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
}
if (index === 0) {
fiber.child = newFiber;
} else if (element) {
prevFiber.siblint = newFiber;
}
if (alternate && alternate.siblint) {
alternate = alternate.siblint;
} else {
alternate = null;
}
// 更新
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
if (fiber.tag === 'class_component') {
reconcileChildren(fiber, fiber.stateNode.render());
} else if (fiber.tag === 'function_component') {
reconcileChildren(fiber, fiber.stateNode(fiber.props));
} else {
reconcileChildren(fiber, fiber.props.children);
}
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
实现类组件状态更新功能
组件状态更新需要给每一个节点构建 fiber 对象,在组件的实例对象上添加 partialState。
执行任务的时候,找到对应的类组件,更新 state。
测试用例
class Greating extends Component {
constructor (props) {
super(props);
this.state = {
name: '张三'
}
}
render () {
return (
<div>
<div>Hello React Component: { this.state.name }</div>
<button onClick={() => this.setState({ name: '李四' })}>button</button>
</div>
)
}
};
render(<Greating />, document.getElementById('root'));
src/react/component/index.js
import { scheduleUpdate } from '../reconciliation/index';
export class Component {
constructor (props) {
this.props = props;
}
setState (partialState) {
scheduleUpdate(this, partialState);
}
}
src/react/misc/getRoot/index.js
export default function getRoot (instance) {
let fiber = instance.__fiber;
while (fiber.parent) {
fiber = fiber.parent;
}
return fiber;
}
src/react/reconciliation/index.js
import { updateNodeElement } from "../dom";
import { createTaskQueue, arrified, createStateNode, getTag, getRoot } from "../misc";
const taskQueue = createTaskQueue();
let subTask = null;
let pendingCommit = null;
const commitAllWork = fiber => {
// 循环 effects 数组,构建 DOM 节点树
fiber.effects.forEach(item => {
if (item.tag === 'class_component') {
item.stateNode.__fiber = item;
}
if (item.effectTag === 'delete') {
item.parent.stateNode.removeChild(item.stateNode);
} else if (item.effectTag === 'update') {
if (item.type === item.alternate.type) {
// 节点类型相同
updateNodeElement(item.stateNode, item, item.alternate);
} else {
// 节点类型不同
item.parent.stateNode.replaceChild(
item.stateNode,
item.alternate.stateNode
)
}
} else if (item.effectTag === 'placement') {
let fiber = item;
let parentFiber = item.parent;
while (parentFiber.tag === 'class_component' || parentFiber.tag === 'function_component') {
parentFiber = parentFiber.parent;
}
if (fiber.tag === 'host_component') {
parentFiber.stateNode.appendChild(fiber.stateNode);
}
}
});
// 备份旧根 fiber 对象
fiber.stateNode.__rootFiberContainer = fiber;
}
const getFirstTask = () => {
// 从任务队列中获取任务
const task = taskQueue.pop();
// 组件状态更新任务
if (task.from === 'class_component') {
const root = getRoot(task.instance);
task.instance.__fiber.partialState = task.partialState;
return {
props: root.props,
stateNode: root.stateNode,
tag: 'host_root',
effects: [],
child: null,
alternate: root
};
}
// 返回最外层节点的 fiber 对象
return {
props: task.props,
stateNode: task.dom,
tag: 'host_root',
effects: [],
child: null,
alternate: task.dom.__rootFiberContainer
}
}
// 构建子级节点关系
const reconcileChildren = (fiber, children) => {
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null; // 子节点的 vritualDOM 对象
let newFiber = null; // 子级 fiber 对象
let prevFiber = null; // 上一个兄弟 fiber 对象
let alternate = null;
if (fiber.alternate && fiber.alternate.child) {
alternate = fiber.alternate.child;
}
while (index < numberOfElements || alternate) {
// 子级 Virtual DOM 对象
element = arrifiedChildren[index];
if (!element && alternate) {
// 删除操作
alternate.effectTag = 'delete';
fiber.effects.push(alternate);
} else if (element && alternate) {
// 更新操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'update',
parent: fiber,
alternate
}
if (element.type === alternate.type) {
// 类型相同
newFiber.stateNode = alternate.stateNode;
} else {
// 类型不同
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
}
} else if (element && !alternate) {
// 初始渲染操作
// 子级 fiber 对象
newFiber = {
type: element.type,
props: element.props,
tag: getTag(element),
effects: [],
effectTag: 'placement',
parent: fiber
}
// 为 fiber 节点添加 DOM 对象或组件实例对象
newFiber.stateNode = createStateNode(newFiber);
}
if (index === 0) {
fiber.child = newFiber;
} else if (element) {
prevFiber.siblint = newFiber;
}
if (alternate && alternate.siblint) {
alternate = alternate.siblint;
} else {
alternate = null;
}
// 更新
prevFiber = newFiber;
index++;
}
}
const executeTask = fiber => {
if (fiber.tag === 'class_component') {
if (fiber.stateNode.__fiber && fiber.stateNode.__fiber.partialState) {
fiber.stateNode.state = {
...fiber.stateNode.state,
...fiber.stateNode.__fiber.partialState
};
}
reconcileChildren(fiber, fiber.stateNode.render());
} else if (fiber.tag === 'function_component') {
reconcileChildren(fiber, fiber.stateNode(fiber.props));
} else {
reconcileChildren(fiber, fiber.props.children);
}
if (fiber.child) {
return fiber.child;
}
let currentExecutelyFiber = fiber;
while (currentExecutelyFiber.parent) {
currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
currentExecutelyFiber.effects.concat([currentExecutelyFiber])
);
if (currentExecutelyFiber.siblint) {
return currentExecutelyFiber.siblint;
}
currentExecutelyFiber = currentExecutelyFiber.parent;
}
pendingCommit = currentExecutelyFiber;
}
const workLoop = deadline => {
if (!subTask) {
subTask = getFirstTask();
}
// 如果任务存在并且浏览器有空闲时间就调用
while (subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask);
}
if (pendingCommit) {
// 执行初始渲染
commitAllWork(pendingCommit);
}
}
const performTask = deadline => {
// 执行任务
workLoop(deadline);
// 如果任务是否存在
if (subTask || !taskQueue.isEmpty()) {
requestIdleCallback(performTask);
}
}
export const render = (element, dom) => {
// 1. 向任务队列中添加任务
taskQueue.push({
dom,
props: { children: element }
});
// 2. 指定在浏览器空闲时执行任务
requestIdleCallback(performTask);
}
export const scheduleUpdate = (instance, partialState) => {
taskQueue.push({
from: 'class_component',
instance,
partialState
});
requestIdleCallback(performTask);
}