0、虚拟dom与diff算法
什么是虚拟dom
用JavaScript对象表示DOM信息和结构,当状态变更的时候,重新渲染这个JavaScript的对象结构,这个JavaScript对象称为virtual dom;
为什么使用虚拟dom
DOM操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能.相对于DOM对象,js对象处理起来更快,而且更贱哒.通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行dom操作,从而提高性能.
虚拟dom如何工作
react中用jsx语法描述试图,通过babel-loader转译后他们变成React.c re a te E le ment()形式,该函数将生成vdom来描述真是dom.将来如果有状态变化,vdom将作出相应的变化,再通过diff算法对比新旧vdom区别,从而作出最终dom操作
diff算法策略
1、同级比较,web ui中dom节点跨层级的移动操作特别少,可以忽略不计 2、拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的属性结构 例如div->p, CompA->CompB 3、对于同一层级的一组子节点,通过唯一的key进行区分
1、JSX与JS对比
代码来源 JSX写法
class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);
JS写法
class HelloMessage extends React.Component {
render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
}
ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
其中对比我们发现Reac转换t中比较重要的两个方法是React.createElement、ReactDOM.render,下面我就就分别对两个方法进行分析.
2、React.createElement
创建kreact.js代码如下,代码中打印数据如图1
// kreact.js
function createElement(type, props, ...children) {
props.children = children;
delete props._source;
delete props._self;
// type: 标签类型如div
// vtype: 组件的类型
let vtype;
if (typeof type === "string") {
vtype = 1;
} else if (typeof type === "functio") {
if (type.isClassComponent) {
// 类组件
vtype = 2;
} else {
// 函数组件
vtype = 3;
}
}
return createVNode(vtype, type, props)
}
export default {createElement}
export class Component {
// 区分组件是function还是class
static isClassComponent = true;
constructor(props) {
this.props = props;
this.state = {};
}
setState() {
}
}
3、ReactDOM.render
我们把其中虚拟dom转dom的代码单独提取出来,那这里面的源码稍微简单
// kreact-domjs
function render(vnode, container) {
const node = initVNode(vnode);
container.appendChild(node);
}
export default {render};
虚拟dom单独提取成initVNode
// kvdom.js
// vtype元素类型: 1-html元素 2-function组件 3-class组件
export function createVNode(vtype, type, props) {
const vnode = {vtype, type, props}
return vnode
}
// 转换虚拟dom为dom
export function initVNode(vnode) {
const {vtype} = vnode;
if(!vtype) {
// 文本节点
return document.createTextNode(vnode);
}
if (vtype === 1) {
// 原生元素
return createElement(vnode)
} else if (vtype === 2) {
// class元素
return createClassComp(vnode)
} else if (vtype === 3) {
// function元素
return createFuncComp(vnode)
}
}
function createElement() {
// 根据type创建元素
const { type, props } = vnode;
const node = document.createElement(type);
// 处理属性
const { key, chidren, ...rest } = props;
Object.keys(rest).forEach((k) => {
// 处理特别属性名: className
if (k === "className") {
node.setAttribute("class", rest[k]);
} else if (k === "style" && typeof rest[k] === "object") {
const style = Object.keys(rest[k])
.map((s) => s + ":" + rest[k][s])
.join(";");
node.setAttribute("style", style);
} else if (k.startsWith("on")) {
// conClick
const event = k.toLowerCase();
node[event] = rest[k];
} else {
node.setAttribute(k, rest[k]);
}
});
// 递归自元素
chidren.forEach((c) => {
if (Array.isArray(c)) {
c.forEach((n) => node.appendChild(initVNode(n)));
} else {
node.appendChild(initVNode(c));
}
node.appendChild(initVNode(v));
});
return node;
}
// class组件转换
function createClassComp() {
// type是class组件声明
const {type, props} = vnode;
const component = new type(props);
const vdom = component.render();
return initVNode(vdom)
}
function createFuncComp() {
// type是函数
const {type, props} = vnode;
const vdom = type(props);
return initVNode(vdom);
}
那么到这里一个简易版的react解析就完成了,后面有机会再补充下...