原理:
react 在内存中生成维护一个跟真实DOM一样的虚拟DOM 树,在改动完组件后,会再生成一个新得DOM,react 会把新虚拟DOM 跟原虚拟DOM 进行比对,找出两个DOM 不同的地方diff ,然后把diff放到队列里面,然后批量更新diff 到真实DOM 上
优点:最终真实DOM 就只更新了diff 部分,提高了渲染速度
我们在前端面试的时候,经常会被问到什么是虚拟DOM。这个概念,感觉很熟悉,但又说不出它到底是什么。现在我们来探索一下到底什么是虚拟DOM。
首先我们看下什么是DOM,对于DOM,我们应该都很熟悉了,下面是MDN对于DOM的定义
文档对象模型 (DOM) 将 web 页面与到脚本或编程语言连接起来。通常是指 JavaScript,但将 HTML、SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分。DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。
虚拟DOM自然就是跟DOM有很大关系的了。我们在使用原生JS开发或者使用Jquery开发,经常就会操作DOM,但是我们使用的时候发现,每次我们改变DOM的时候,页面再次渲染,会花费不短的一段时间,这样用户体验就不太好了。如果我们每次操作的不是DOM或者每次只操作更少的DOM呢,是不是会花费的时间更短呢,基于这个想法,就有了虚拟DOM。
在React中,会把DOM转换成JavaScript对象,然后再把JavaScript对象转化成DOM,这样我们对于DOM的操作,实际上是在操作这个JavaScript对象。
我们利用在线babel工具来看下。左边是JSX,右边是JSX经过babel转换后的效果,事实上JSX是右边这种写法的语法糖,我们在React项目中写的JSX的写法都会转换成右边这种写法。
在React项目中,使用以下这种写法,渲染出的效果也是一样的。
import React from 'react';
import ReactDOM from 'react-dom';
let element = React.createElement("h1", {
id: "test",
className: "testClass"
}, "test");
ReactDOM.render(element, document.getElementById('root'));
现在我们来分析以下上面的代码
在上面的代码中加入console.log(element),打印出element的值,然后看到,原来某个值是这样的:
3.模拟实现React.createElement()
有上图可以这个对象有多个属性,目前来说对我们比较重要的是props和type属性,所以先实现对于这两个属性的操作。
React.createElement()接收3个参数,现在要把这3个参数合并到type和props中。
React.createElement()接收3个以上参数,说明该元素里面有多个子元素(这些子元素仍然是React.createElement()),那么把第二个参数后面的所有参数转换成数组放入children中
function ReactElement(type, props) {
const element = { type, props };
return element;
}
function createElement(type, config = {}, children) {
let propName;
const props = {}; // 定义props
for(propName in config) {
props[propName] = config[propName]; // 复制config的属性到props中
}
// 处理children
const childrenLength = arguments.length - 2;
if(childrenLength === 1) {
props.children = children;
} else {
// 有多个子元素的情况
props.children = Array.from(arguments).slice(2);
}
return ReactElement(type, props);
}
加入以下代码测试下效果
const element = createElement("h1", {
id: "test",
className: "testClass"
}, createElement("span", null, "span1"), createElement("span", null, "span2"));
console.log(JSON.stringify(element))
打印结果为:
可以看到,最终,DOM转换成了JavaScript对象。