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,
...
};
這里主要實(shí)現(xiàn)3個(gè)最常用的API:
React.createElement // 返回虛擬dom
React.Component // 實(shí)現(xiàn)組件的自定義
ReactDOM.render // 創(chuàng)建真實(shí)dom
1喧兄、React.createElement
我們在打包編譯react項(xiàng)目的時(shí)候盲再,實(shí)際上會(huì)有一個(gè)jsx轉(zhuǎn)換成普通的js代碼的過程。
如jsx:
會(huì)被轉(zhuǎn)換為:
/**
* 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個(gè)參數(shù)(超過3個(gè)時(shí)會(huì)從arguments取)撒璧,返回了ReactElement函數(shù)的運(yùn)行結(jié)果 ,其實(shí)返回的就是vdom笨使。
按照這個(gè)邏輯卿樱,可以實(shí)現(xiàn)一個(gè)簡化版的createElement。
實(shí)現(xiàn)一個(gè)簡單的createElement:
function createElement(type, props, ...children) {
return {
type,
props,
children
}
}
看起來過于簡潔了... 但是對(duì)于簡單實(shí)現(xiàn)已經(jīng)夠用了 :)硫椰。
2繁调、ReactDOM.render
export function render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
// ...省略
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
react里的render的功能還是非常復(fù)雜的,涉及比較多的代碼靶草,這里只能展示一小部分蹄胰。
對(duì)于簡化版的render, 接收2個(gè)參數(shù):vnode奕翔、根節(jié)點(diǎn)裕寨,然后將vnode轉(zhuǎn)換成真實(shí)節(jié)點(diǎn)插入根節(jié)點(diǎn)。對(duì)于vnode的類型派继,分為3類來處理:字符串宾袜、函數(shù)、原生html節(jié)點(diǎn)驾窟。
實(shí)現(xiàn)一個(gè)簡單的render:
function render(vnode, container) {
return container.appendChild(createDom(vnode));
}
// 將vdom轉(zhuǎn)換為真實(shí)dom
function createDom(vnode) {
// 純字符 直接創(chuàng)建文件節(jié)點(diǎn)
if (typeof vnode === 'string') {
const node = document.createTextNode(vnode);
return node;
}
// 處理 函數(shù)和類組件
if (typeof vnode.tag === 'function') {
return createComponentDom(vnode.tag, vnode.attrs)
}
// 處理原生節(jié)點(diǎn)
const node = document.createElement(vnode);
if (vnode.props) {
Object.keys(node.props).forEach((k) => {
setAttr(node, k, vnode.props[key]);
})
}
// 遞歸處理所有的子節(jié)點(diǎn)
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庆猫、實(shí)現(xiàn)component
在使用react的時(shí)候,類組件總是要繼承component纫普,并且經(jīng)常要使用setState這個(gè)方法 下面先看一下 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');
};
可以看到這是一個(gè)構(gòu)造函數(shù)阅悍,并且setState這個(gè)方法里最終執(zhí)行了
this.updater.enqueueSetState(...)
實(shí)際上setState就是異步的。按照源碼昨稼,下面來實(shí)現(xiàn)一個(gè)簡單的component节视。
實(shí)現(xiàn)一個(gè)簡單的component
class component {
// 標(biāo)識(shí)類組件
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) {
// 類組件 創(chuàng)建實(shí)例
const instance = new component();
node = renderComponent(instance);
} else {
// 函數(shù)組件 直接運(yùn)行得到vdom
const vnode = component(props);
node = createDom(vnode);
}
return node;
}
// 傳入類組件的實(shí)例,渲染類組件
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;
}
至此假栓,一個(gè)簡易的react算是完成了寻行。