React源码学习:Virtual DOM 与 diff 算法
p8-p14: virtual dom/diff
JSX 到底是什么
- 长得像HTML,实际上是 js 代码
- 是一种语法糖
- 所有节点会被编译成 createElment 的方法的调用
DOM 操作有什么问题
- 用 js 直接操作 dom 性能,远低于其它 js 对象
- 所以有 vdom 的出现,先操作 vdom,等改变确定之后,再用 vdom 告诉真实的 dom 哪些需要变化,从而提升性能
什么是 VirtualDOM
- 他的出现是为了提升 js 操作 dom 的效率
- 用 js 对象表示真实 dom 的信息
利用 babel 将 jsx的解析器换成 TinyReact
- 在代码中添加注释:/* @jsx TinyReact.createElement */
- 在
.babelrc
配置中添加 progma: “TinyReact.createElement” (具体自行搜索) -核心是这一句 pragma: 'TinyReact.createElement'
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
"@babel/preset-env",
[
'@babel/preset-react',
{
pragma: 'TinyReact.createElement',
},
],
],
},
},
}
/* @jsx TinyReact.createElement */
<h1 model={{ role:'header' }}>
it works
<img title="logo" src="https://js.work/logo.png" />
</h1>
"use strict";
/* @jsx TinyReact.createElement */
TinyReact.createElement("h1", {
model: {
role: 'header'
}
}, "it works", TinyReact.createElement("img", {
title: "logo",
src: "https://js.work/logo.png"
}));
实现一个 createElement 方法<另外要做的3件事情>
- 需要改进的点:文本节点
- 文本本节点(目前jsx是直接以字符串输出),需要处理成 props:{ textContent: ‘text’ } 这种形式就行
- 看 text 当中为: true/false 值,处理一下
- true: 显示
- false/null: 直接从节点中T出
- 将
children
添加到 props中
/**
* 由JSX,创建 vdom 对象
* @param type
* @param props
* @param children
* @returns {{children: *[], type, props}}
*/
function createElement(type, props, ...children) {
return {
type,
props,
children
};
}
export default createElement;
实操代码如下
- 原理代码
- 处理上述3个问题后的代码(实测试只有2个问题)
- 处理文本节点的问题
// 将 ['HelloReact'] 这种 转化为{type:"text", props:{textContent:child}}
- 处理不中出现的 true/false值情况,实测,只要filter false 就可以了
- 最终处理的
vdom
结果
// 这一行是必须的
import TinyReact from './tiny-react';
const vdom = (
<div className="container">
<h1>你好 Tiny React- </h1>
<h2 data-test="mtest">(编码必杀技)</h2>
<div>
嵌套1 <div>嵌套 1.1</div>
</div>
<h3>(观察:这个将会被改变)</h3>
{2 == 1 && <div>如果2和1相等渲染当前内容</div>}
{2 == 2 && <div>2</div>}
<span>这是一段内容</span>
<button onClick={() => alert('你好')}>点去我</button>
<h3>这个将会被删除</h3>
2, 3
<input type="text" value="13" />
</div>
);
console.log('vdom: ', vdom);
export default function createElement(type, props, ...children) {
//问题1: 将 ['HelloReact'] 这种 转化为{type:"text", props:{textContent:child}}
let newChildren = children.map((child) => {
if (typeof child === 'string') {
return createElement('text', { textContent: child });
}
return child;
});
// 问题2: 去掉 false 值
newChildren = newChildren.filter(Boolean);
// 问题3: 让 props 上具有 children 属性
const newProps = Object.assign({ children: newChildren }, props);
return {
type,
props: newProps,
children: newChildren,
};
}
将 vdom 转化为 真实 dom: render
- 先处理 type,添加到页面
- 处理 props,添加属性
- 普通属性 setAttribute
- disabled/value 等 props,动态属性
- hooks:className/for 等属性
- 整件属性: onClick 这种: propName.slice(0,2) === ‘on’
- children 这种情况:因为他是子元素,不是属性
- 处理 children
参考