首先 说下原理
react自v16以后发生了很多变化,v16以后底层的“虚拟DOM”不再是简单JSON数据了,React采用了最新的Fiber(双向链表)的数据结构,作为“协调”(Diff)运算的基础数据。React背后还提供了强大的 react-reconciler 和 scheduler 库实现Fiber链表的生成、协调与调度。相比vue组件,react在较大组件方面的性能更高。如果要手写一个简易版本的React,其核心要实现以下功能,createElement(用于创建元素)、createDOM/updateDOM(用于创建和更新DOM)、render/workLoop(用于生成Fiber和协调运算)、commitWork(用于提交)等,如果还有支持Hooks,还得封闭Hooks相关的方法。
思路
-
下载react官网仓库中的代码,搞清楚目录结构、各个包的作用。
-
下载地址:https://github.com/facebook/react,进一步查看packages目录。
-
react-dom 这是DOM渲染的若干功能。
-
react 这是React核心语法及其API封装的包
-
react-reconciler 用于生成“Fiber树”和“协调运算”的。
-
scheduler,它是模拟requestIdleCallback(fn)的兼容性实现,用于执行复杂的任务,当浏览器主线程有“空闲”时执行这些复杂的任务,不霸占浏览器主线程。
-
用工程化环境或者HTML页面,引入react.js和react.dom.js,在源码中进行调试学习。
-
如果采用HTML页面的方式来分析React源码,还要引入babel.js,对JSX语法进行编译,在script标签还要添加 type=‘text/babel’。
-
慢慢通过删减、调试的方式,把react.js中无用的逻辑都删除,得到一个mini-react。
我们需要什么
实现一个简易react
需要用到babel.min.js
react.development.js
react-dom.development.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React</title>
</head>
<body>
<div id="app"></div><hr>
<div id='root'></div>
<script src='./dist/babel.min.js'></script>
<script src='./dist/react.development.js'></script>
<script src='./dist/react-dom.development.js'></script>
<script type='text/javascript'>
// 创建了一个React元素
const app = React.createElement('h1', {
title:'testReact'}, 'Hello React')
ReactDOM.render(app, document.getElementById('app'))
</script>
<script type='text/babel'>
const App = () => {
const [num, setNum] = React.useState(0)
return (
<div>
<h1>{
num }</h1>
<button onClick={
()=>setNum(num-1)}>自减</button>
<button onClick={
()=>setNum(num+1)}>自增</button>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
</script>
</body>
</html>
首先实现基本的render和react-dom
// return Fiber基础单元 = { type, props: { children } }
// type 表示当前节点的HTML元素名,也有可能是'TEXT-ELEMENT'
// props 表示当前节点的jsx属性,还包括一个特殊属性children
function createElement(type, props, ...children) {
// 返回一个Fiber单元
return {
type,
props: {
...props,
children: children.map(ele=>(typeof ele==='object') ? ele : createTextElement(ele))
}
}
}
// 文本Fiber
function createTextElement(text) {
return {
type: 'TEXT_ELEMENT',
props: {
nodeValue: text,
children: []
}
}
}
function isProperty (key) {
return key !== 'children'
}
function render (element, container) {
const dom =
element.type === 'TEXT_ELEMENT'
? document.createTextNode(element.props