关于react源码,很早就有参阅的想法了,奈何每次实践都是浅尝则止,思考了很久:与其预留时间统一观阅学习,不如手起刀落 现在跟学总结经验;
repo:https://github.com/CNZN/myreact
目的:了解react基本思想,对常用api原理的剖析及实现, 希望对React 的基本逻辑及实现思路有了大概的掌握;
预想困难:
实现流程:基于babel转义的js得到dom虚拟对象,分析creatElement、render的实现
jsx代码
//如若想让babel用自写的js去解析需要加上下面这行
/** @jsx MyReact.createElement */
const element = <div title="foo">hello</div>
babel转译后的代码
"use strict";
//### 这是babelES6转译ES5自动加上的,使用严格模式的意思;
const element = /*#__PURE__*/React.createElement("div", {
title: "foo"
}, "hello");
//"Pure" 注入支持
//转换后的 ES6 类将使用 /*#__PURE__*/ 进行注释。
//这允许给像 Uglify 和 babel-minify 这样的 minfiers
//在删除无用代码时提供提示。这些注释也会被添加到其他辅助函数中。
react.createElement是把jsx语法转换为虚拟dom,也就是js对象存于内存中,可以根据入参写一下createElement
1、creatElement
function createElement (type, props, ...children) {
return {
type,
props: {
...props,
children: children.map(child =>
typeof child === 'object'
? child
: createTextElement(child)
)
}
}
}
function createTextElement (text) {
return {
// 判断文本节点
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
// 无子节点
children: []
}
}
}
2、render
经过createElement之后element的实际值为
这样的数据格式就再清楚不过了,岂不是和创建路由类似?children里存放子节点,子节点自带属性,且也可能有子节点
function render (element, container) {
// 这里通过拿到的vdom生成真实dom节点
const dom = element.type === 'TEXT_ELEMENT'
? document.createTextNode("")
: document.createElement(element.type)
// 有子节点的话递归
element.props.children.forEach(child =>
render(child, dom)
)
const isProperty = key => key !== "children"
Object.keys(element.props)
.filter(isProperty)
.forEach(name => {
// 将属性付给节点
dom[name] = element.props[name]
})
container.appendChild(dom)
}
3、fiber
为了让渲染工作可以随场景而暂停,将渲染工作小化,react设计了fiber,代表着一个数据结构或者一个工作单元。
此处可以理解为浏览器空闲与否会添加一个全局变量,在renderdom的函数中起阀门作用
// 创建 fiber
const newFiber = {
type: element.type,
props: element.props,
parent: fiber,
dom: null,
}
4、hooks
每次使用钩子时(以useState为例),会在初始化时可以接受初始值,且返回初始状态及状态改变的方法,方法可以为函数.
思路大致为
function useState (init) {
//首先会判断是更新,上一次vdom对象是否有值
const oldhook = wipFiber.alternate && wipFiber.alternate.hook;
const hook = {
state: oldhook ? oldhook.state : init
//存放状态修改逻辑
queue: []
}
// 从旧的钩子队列中获取所有动作,然后将它们一一应用到新的钩子状态
const actions = oldHook ? oldHook.queue : []
actions.forEach(action => {
hook.state = action(hook.state)
})
const setState = behavior => {
hooks.queue.push(behavior)
}
return [hook.state, setState]
}
一揽