1.什么是虚拟DOM
我们在前端面试的时候,经常会被问到什么是虚拟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对象。
2.DOM是如何创建虚拟DOM的
我们利用在线babel工具来看下。左边是JSX,右边是JSX经过babel转换后的效果,事实上JSX是右边这种写法的语法糖,我们在React项目中写的JSX的写法都会转换成右边这种写法。
在React项目中,使用以下这种写法,渲染出的效果也是一样的。
1
2
3
4
5
6
7
8
9
10
11 1import React from 'react';
2import ReactDOM from 'react-dom';
3
4let element = React.createElement("h1", {
5 id: "test",
6 className: "testClass"
7 }, "test");
8
9ReactDOM.render(element, document.getElementById('root'));
10
11
现在我们来分析以下上面的代码
- React.createElement()方法传入了3个参数,第1个参数对应的是标签名称,第2个参数是属性,第三个参数是内容,然后返回某个值
- ReactDOM.render()方法接收了两个参数,第一个参数是刚刚提到的某个值,第二个参数是获取到的root元素,对应的是index.html中的<div id="root"></div>
在上面的代码中加入console.log(element),打印出element的值,然后看到,原来某个值是这样的:
由此说明:React.createElement()方法创建了虚拟DOM。
3.模拟实现React.createElement()
有上图可以这个对象有多个属性,目前来说对我们比较重要的是props和type属性,所以先实现对于这两个属性的操作。
React.createElement()接收3个参数,现在要把这3个参数合并到type和props中。
React.createElement()接收3个以上参数,说明该元素里面有多个子元素(这些子元素仍然是React.createElement()),那么把第二个参数后面的所有参数转换成数组放入children中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1function ReactElement(type, props) {
2 const element = { type, props };
3 return element;
4}
5
6function createElement(type, config = {}, children) {
7 let propName;
8 const props = {}; // 定义props
9 for(propName in config) {
10 props[propName] = config[propName]; // 复制config的属性到props中
11 }
12
13 // 处理children
14 const childrenLength = arguments.length - 2;
15 if(childrenLength === 1) {
16 props.children = children;
17 } else {
18 // 有多个子元素的情况
19 props.children = Array.from(arguments).slice(2);
20 }
21
22 return ReactElement(type, props);
23}
24
25
加入以下代码测试下效果
1
2
3
4
5
6
7
8 1const element = createElement("h1", {
2 id: "test",
3 className: "testClass"
4 }, createElement("span", null, "span1"), createElement("span", null, "span2"));
5
6console.log(JSON.stringify(element))
7
8
打印结果为:
可以看到,最终,DOM转换成了JavaScript对象。