手写简易React(手写系列六)

微信扫一扫,分享到朋友圈

手写简易React(手写系列六)

背景

以下是手写系列内容预告,初步计划一周一个主题。

JSX与虚拟DOM

我们在使用React的时候,源码里写的都是JSX。JSX代码在运行之前会被Babel的@babel/preset-react 预设里的插件转换成JavaScript后再运行。

let div = <div className="header">hello jirengu</div>;

转换后的JavaScript

let div = React.createElement("div", {className: "header"}, "hello jirengu");

动手试试看!

上面的代码没法运行,因为目前还不存在React.createElement这个东西。我们补充代码,提前增加一个React对象,里面包含createElement方法。这个方法接收输入参数,返回一个对象。

const React = {
createElement(tag, attrs, ...children) {
return {
tag,
attrs,
children
}
}
};
//let div = React.createElement("div", {className: "header"}, "hello jirengu");
let div = <div className="header">hello jirengu</div>;
console.log(div);
/*
{
tag: "div",
attrs:{className: "header"},
children: ["hello"]
}
*/

在【这里】看看控制台的输出。输出的这个div已经变成一个对象,包含原始JSX的全部信息。这个对象就叫做 虚拟DOM
(即:假的,目前还不存在的,但后面要被渲染放到页面变成真实DOM的对象)。

虚拟DOM的渲染

function render(vdom, container) {
let node;
if(typeof vdom === 'string') {
node = document.createTextNode(vdom);
}
if(typeof vdom === 'object') {
node = document.createElement(vdom.tag);
vdom.children.forEach(childVdom => render(childVdom, node));
}
setAttribute(node, vdom.attrs);
container.appendChild(node);
}
function setAttribute(node, attrs) {
for(let key in attrs) {
if(key.startsWith('on')) {   //<p onClick={xxx}></p>
node[key.toLocaleLowerCase()] = attrs[key];
} else if(key === 'style') {  //<p style={xxx}></p>
Object.assign(node.style, attrs[key]);
} else {
node[key] = attrs[key];    //<p className={xxx}></p>
}
}
}
const ReactDom = {
render(vdom, container) {
container.innerHTML = '';
render(vdom, container);
}
};

上述代码中,render函数的作用是把虚拟DOM对象渲染后放入到container元素内。为了便于调用,我们新建一个ReactDom对象,包含一个render方法,作用是清空容器,渲染虚拟DOM到容器。

把以上代码做一个整理

const React = {
createElement(tag, attrs, ...children) {
return {tag, attrs, children};
}
};
const ReactDom = {
render(vdom, container) {
container.innerHTML = "";
render(vdom, container);
}
};
function render(vdom, container) {
let node;
if (typeof vdom === "string") {
node = document.createTextNode(vdom);
}
if (typeof vdom === "object") {
node = document.createElement(vdom.tag);
setAttribute(node, vdom.attrs);
vdom.children.forEach((childVdom) => render(childVdom, node));
}
container.appendChild(node);
}
function setAttribute(node, attrs) {
for (let key in attrs) {
if (key.startsWith("on")) {
node[key.toLocaleLowerCase()] = attrs[key];
} else if (key === "style") {
Object.assign(node.style, attrs[key]);
} else {
node[key] = attrs[key];
}
}
}
//测试代码
let str = "jirengu";
let styleObj = {
color: "red",
fontSize: "30px"
};
ReactDom.render(
<div className="wrap">
Hello {str}
<button className="btn" onClick={() => console.log("click me")}>
Click me!
</button>
<p style={styleObj}>I have style</p>
</div>,
document.body
);

可在【这里】运行测试效果。

以上代码是一个最粗浅的React雏形。自己后续可尝试实现Class组件、setState、函数组件、DOM diff、Hooks等。下方是视频教程和源码可供参考(源码可切换分支看一步一步实现的过程)。

微信扫一扫,分享到朋友圈

手写简易React(手写系列六)

从四个问题透析Linux下C++编译&链接

上一篇

TPAMI 2020 | 高分辨率网络对计算机视觉任务的影响

下一篇

你也可能喜欢

手写简易React(手写系列六)

长按储存图像,分享给朋友