Vitual Dom
-
概念 用 JavaScript 对象表示 DOM 信息和结构,当状态变更更的时候,重新渲染这个 JavaScript 的对象结构。这个JavaScript 对象称为virtual dom。
-
原因 DOM操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能。相对于DOM对象,js对象处理起来更快,而且更简单。通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行dom操作,从⽽提⾼性能。
3.在react中的使用
react中用JSX语法描述视图,babel-loader将JSX转译为React.createElement(...)形式,该函数将生成vdom来描述真实dom。将来如果状态变化,vdom将作出相应变化,再通过diff算法对比新⽼vdom区别从⽽而做出最终dom操作。
JSX形式:
class HelloMessage extends React.Component {
render() {
return (
<div key="0">
<div className={hello} key="1" >
Hello {this.props.name}
</div>
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);
babel-loader编译后:分别传入标签类型,props,常量,变量,嵌套的children~
class HelloMessage extends React.Component {
render() {
return React.createElement(
"div",
{ key: "0" },
React.createElement(
"div",
{ className: hello, key: "1" },
"Hello ",
this.props.name
)
);
}
}
ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
4. React.createElement()的作用:将传入的元素转换成虚拟DOM
核心API
- React.createElement:创建虚拟DOM (1)节点类型:⽂文本节点,HTML标签节点,function组件,class组件,fragment,其他如portal等节点。
在外部index.js文件中定义各种节点类型:
// import React from "react";
// import ReactDOM from "react-dom";
import React from "./kreact/";
import ReactDOM from "./kreact/ReactDOM";
import Component from "./kreact/Component";
import "./index.css";
// jsx=>createElement(生成element,就是我们需要的虚拟dom)=>render(vnode->node, 再把node渲染到container)
function FunctionComponent({name}) {
return (
<div className="border function">
hello, {name}
<button onClick={() => console.log("omg")}>click</button>
</div>
);
}
class ClassComponent extends Component {
render() {
const {name} = this.props;
return <div className="border function">hello, {name}</div>;
}
}
const jsx = (
<div className="border">
<p>这是一个文本</p>
<a href="https://kaikeba.com/">开课吧</a>
<div className="border">
<h5>hello</h5>
</div>
<FunctionComponent name="function" />
<ClassComponent name="class" />
<>
<h5>文本1</h5>
<h5>文本2</h5>
</>
{[1, 2, 3].map(item => {
return (
<div className="border" key={item}>
<p>{item}</p>
<p>{item}</p>
</div>
);
})}
</div>
);
// element, container
// vnode->node , 把node渲染更新到container
ReactDOM.render(jsx, document.getElementById("root"));
在内部index.js中定义createElement
function createElement(type, props, ...children) {
// 删除原生
if (props) {
delete props.__source;
delete props.__self;
}
return {
type: type,
props: {
...props,
//!这里的处理与源码稍有不同,源里的话,只有一个元素,children是对象,多于一个的时候,是数组
children: children.map(child =>
typeof child === "object" ? child : createTextNode(child)
)
}
};
}
// p标签TEXT类型要单独处理生成树
function createTextNode(text) {
return {
type: "TEXT",
props: {
children: [],
nodeValue: text
}
};
}
export default {
createElement
};
- React.Component:实现⾃自定义组件,被其他class组件继承
export default class Component {
static isReactComponent = {};
constructor(props) {
this.props = props;
}
}
- ReactDOM.render:渲染真实DOM,将虚拟dom转换为真实dom,将真实dom挂载到container标签下。
在ReactDom.js定义render函数。
function render(vnode, container) {
// console.log("vnode", vnode);
// 虚拟dom转换为真实dom
const node = createNode(vnode);
//把node更新到container
container.appendChild(node);
}
// 根据vnode,创建一个node
function createNode(vnode) {
const {type, props} = vnode;
let node;
// 根据node的类型调用document.createXXX()创建节点,function和class类型要单独处理
if (typeof type === "function") {
node = type.isReactComponent ?
updateClassComponent(vnode)
: updateFunctionComponent(vnode);
} else if (type === "TEXT") {
node = document.createTextNode("");
} else if (type) {
node = document.createElement(type);
} else {
node = document.createDocumentFragment();
}
// 将属性添加进节点里
updateNode(node, props);
// 递归将children生成真实节点
reconcilerChildren(props.children, node);
return node;
}
function reconcilerChildren(children, node) {
for (let i = 0; i < children.length; i++) {
let child = children[i];
// 遍历 创建元素
// 判读children[i]类型
if (Array.isArray(child)) {
for (let j = 0; j < child.length; j++) {
// 递归调用render
render(child[j], node);
}
} else {
// 递归调用render
render(children[i], node);
}
}
}
// 更新节点上属性,如className、nodeValue等
function updateNode(node, nextVal) {
Object.keys(nextVal)
.filter(k => k !== "children")
.forEach(k => {
if (k.slice(0, 2) === "on") {
// 以on开头,就认为是一个事件,源码处理复杂一些,
let eventName = k.slice(2).toLocaleLowerCase();
node.addEventListener(eventName, nextVal[k]);
} else {
node[k] = nextVal[k];
}
});
}
// function组件,返回node
function updateFunctionComponent(vnode) {
const {type, props} = vnode;
// 运行函数返回虚拟dom
const vvnode = type(props);
const node = createNode(vvnode);
return node;
}
function updateClassComponent(vnode) {
const {type, props} = vnode;
//实例化class
const cmp = new type(props);
// 调用class中的render 返回虚拟dom
const vvnode = cmp.render();
const node = createNode(vvnode);
return node;
}
export default {
render
};