0、简单介绍
react里包含有丰富的api 有兴趣可以看一下 React.js源码:
const React = {
Children: {
map,
forEach,
count,
toArray,
only,
},
createRef,
Component,
PureComponent,
createContext,
forwardRef,
lazy,
memo,
...
useContext,
useEffect,
useMemo,
useReducer,
useRef,
useState,
...
};
这里主要实现3个最常用的API:
React.createElement // 返回虚拟dom
React.Component // 实现组件的自定义
ReactDOM.render // 创建真实dom
1、React.createElement
我们在打包编译react项目的时候,实际上会有一个jsx转换成普通的js代码的过程。
如jsx:
会被转换为:
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
*/
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
// ... 中间省略
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
可以看到react源码里定义的 createElement 接收了3个参数(超过3个时会从arguments取),返回了ReactElement函数的运行结果 ,其实返回的就是vdom。
按照这个逻辑,可以实现一个简化版的createElement。
实现一个简单的createElement:
function createElement(type, props, ...children) {
return {
type,
props,
children
}
}
看起来过于简洁了... 但是对于简单实现已经够用了 :)。
2、ReactDOM.render
export function render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
// ...省略
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
react里的render的功能还是非常复杂的,涉及比较多的代码,这里只能展示一小部分。
对于简化版的render, 接收2个参数:vnode、根节点,然后将vnode转换成真实节点插入根节点。对于vnode的类型,分为3类来处理:字符串、函数、原生html节点。
实现一个简单的render:
function render(vnode, container) {
return container.appendChild(createDom(vnode));
}
// 将vdom转换为真实dom
function createDom(vnode) {
// 纯字符 直接创建文件节点
if (typeof vnode === 'string') {
const node = document.createTextNode(vnode);
return node;
}
// 处理 函数和类组件
if (typeof vnode.tag === 'function') {
return createComponentDom(vnode.tag, vnode.attrs)
}
// 处理原生节点
const node = document.createElement(vnode);
if (vnode.props) {
Object.keys(node.props).forEach((k) => {
setAttr(node, k, vnode.props[key]);
})
}
// 递归处理所有的子节点
vnode.children.forEach(child => {
return render(child, node);
})
return node;
}
function setAttr(node, key, val) {
// 还需要增加判断key的各种情况 如 style htmlFor等等
if (key === 'className') {
node.setAttribute('class', val);
} else {
node.setAttribute(key, val);
}
}
3、实现component
在使用react的时候,类组件总是要继承component,并且经常要使用setState这个方法 下面先看一下 component源码:
/**
* Base class helpers for the updating state of a component.
*/
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
//... 省略部分代码和注释
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
可以看到这是一个构造函数,并且setState这个方法里最终执行了
this.updater.enqueueSetState(...)
实际上setState就是异步的。按照源码,下面来实现一个简单的component。
实现一个简单的component
class component {
// 标识类组件
static isClassComponent = true
constructor(props) {
this.props = props
this.state = {}
}
setState(newState) {
this.state = Object.assign({}, this.state, newState)
renderComponent(this)
}
}
// 返回组件渲染后的dom
function createComponentDom(component, props) {
let node;
if (component.isClassComponent) {
// 类组件 创建实例
const instance = new component();
node = renderComponent(instance);
} else {
// 函数组件 直接运行得到vdom
const vnode = component(props);
node = createDom(vnode);
}
return node;
}
// 传入类组件的实例,渲染类组件
function renderComponent(componentObj) {
let base;
const vnode = componentObj.render();
base = createDom(vnode);
if (componentObj.base && componentObj.base.parentNode) {
componentObj.base.parentNode.replaceChild(base, componentObj.base);
}
componentObj.base = base;
}
至此,一个简易的react算是完成了。