index.js
/**build your own react */
// JSX是如何被解析的?
// JSX通过babel会被解析成 React.createElement
// // 比如
// const element = <h1 >Hello</h1>
// // 他会被解析成
// const element1 = React.createElement(
// 'h1', // 标签名
// { title: 'foo' }, // attributes
// "Hello", // children,一般是数组
// )
// Step1: 实现简易createElement
function createElement(type, props, ...children) {
return {
// 标记 元素类型
type,
// 元素属性
props: {
...props,
// 子元素
children: children.map((child) => {
// 为了区分 基本类型 和引用类型,单独使用 createTextElement来创建文本节点
return typeof child === "object" ? child : createTextElement(child);
})
}
};
}
function createTextElement(text) {
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: []
}
};
}
/**
* var h=document.createElement("H1")
var t=document.createTextNode("Hello World");
h.appendChild(t);
输出 <h1>Hello World </>的效果
*
*/
// Step6
function commitRoot() {
commitWork(wipRoot.child)
// commit阶段完成后,保存当前的fiber树
currentRoot = wipRoot
wipRoot = null
}
function commitWork(fiber) {
if (!fiber) {
return
}
const domParent = fiber.parent.dom
domParent.appendChild(fiber.dom)
commitWork(fiber.child)
commitWork(fiber.sibling)
}
// Step2 我们需要render到真实的dom节点上
function render(element, container) {
// 设置fiber root
wipRoot = {
dom: container,
props: {
children: [element]
},
// 和上一次的commit阶段的 旧fiber 树建立连接
alternate: currentRoot
}
nextUnitOfWork = wipRoot
}
let nextUnitOfWork = null
// work in progress fiber root
let wipRoot = null
let currentRoot = null
// Step3 并发模式
// Step4 fiber
function workLoop(deadline) {
// 是否需要暂停
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
// 执行 一个工作单元并返回下一个工作单元
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
// 判断空间时间是否足够
shouldYield = deadline.timeRemaining() < 1
}
// 所有工作单元都执行完成后,我们一并进行提交操作
// commitRoot里进行所有元素 往dom 树上添加的动作
if (!nextUnitOfWork && wipRoot) {
commitRoot()
}
// 需要暂停响应优先级更高的操作(比如用户交互)
requestIdleCallback(workLoop)
function performUnitOfWork(fiber) {
// 创建一个dom元素,挂载到fiber的dom属性
if (!fiber.dom) {
fiber.dom = createDom(fiber)
}
const elements = fiber.props.children
// reconcileChildren
reconcileChildren(fiber, elements)
}
function reconcileChildren(wipFiber, elements) {
let index = 0
let oldFiber = wipFiber.alternate && wipFiber.alternate.child
// 保存上一次fiber
let preSibling = null
while (index < elements.length || oldFiber != null) {
const element = elements[index]
const newFiber = null
const sameType = oldFiber && element && element.type === oldFiber.type
// 相同节点,属性迁移
if (sameType) { }
// 节点不同,且有新元素,执行新增
if (element && !sameType) { }
// 节点不同,且有旧元素,执行删除
if (oldFiber && !sameType)
// 第一个子元素 作为 child,其余的 子元素 作为 sibling
if (index === 0) {
fiber.child = newFiber
} else {
preSibling.sibling = newFiber
}
// 执行完后将fiber保存在preSibling
preSibling = newFiber
index++
}
// fiber链访问流程: 先孩子,然后是sibling,然后是uncle(uncle是孩子的parent, 是sibling的parent),以此类推
if (fiber.child) {
// 第一个孩子存在
return fiber.child
}
let nextFiber = fiber
while (nextFiber) {
// 其他孩子在react中,被称为fiber的sibling
if (nextFiber.sibling) {
return nextFiber.sibling
}
//
nextFiber = nextFiber.parent
}
}
}
// 给库命名
const MyReact = {
createElement,
render
};
/** @jsx MyReact.createElement */
const element = (
<div style="background: salmon">
<h1>Hello World</h1>
<h2 style="text-align:right">from myReact</h2>
</div>
);
const container = document.getElementById("root");
MyReact.render(element, container);
package.json
{
"name": "react",
"version": "1.0.0",
"description": "React example starter project",
"keywords": [
"react",
"starter"
],
"main": "src/index.js",
"dependencies": {
"react": "17.0.0",
"react-dom": "17.0.0",
"react-scripts": "3.4.3"
},
"devDependencies": {
"typescript": "3.8.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!