什么是虚拟DOM?
虚拟DOM其实就是一个普通的JavaScript对象,包含了tag,props,children三个属性。
<div id="app">
<p class="text">hello world!!!</p>
</div>
上面的 HTML 转换为虚拟 DOM 如下:
{
tag: 'div',
props: {
id: 'app'
},
chidren: [
{
tag: 'p',
props: {
className: 'text'
},
chidren: [
'hello world!!!'
]
}
]
}
该对象就是我们常说的虚拟 DOM 了,因为 DOM 是树形结构,所以使用 JavaScript 对象就能很简单的表示。
为什么需要虚拟DOM?
已经有了DOM了,为什么还需要再引入一个新概念。
为各位几个问题?
「频繁操作DOM不烦吗?」
「频繁操作DOM对网页性能好吗?」
「使用DOM能实现跨平台开发吗?」
如果你在思考上面三个问题之后领悟到了直接通过操作DOM进行开发确实不容易,那么你离理解虚拟DOM又进了一部。
虚拟DOM的出现就是为了解决但不限于以上三个问题。
具体是怎么解决的呢?
改善大规模操作DOM的性能
虚拟DOM可以将多次操作合并为一次操作。(React的setState被设计成异步函数的初衷)
举例:加入我们要向页面中添加1000个div,在不使用vue和react的情况下只能一次一次的添加,而虚拟DOM可以将这1000个节点放在一个数组里,一次性的添加,减少了DOM的操作次数.
缩小DOM操作的范围
虚拟DOM借助DOM diff 可以将多余的操作省掉,只操作那些已经改变的DOM元素
举例,假如页面有990个节点,我们需要再添加10个节点,可是此时我们的数据中原来的和现在的节点放在了一起,此时虚拟DOM可以利用虚拟diff帮我们区分出新老节点,并只操作新的改变了的节点,缩小DOM操作范围。
能以较低成本实现跨平台开发
虚拟DOM不仅可以变成DOM,还可以变成小程序,iOS应用,安卓应用,因为虚拟DOM实际上是一个JS对象。
规避XSS风险
虚拟DOM内部确保了字符转义,因此可以避免XSS跨站脚本攻击,。
虚拟DOM实现原理
因为本人没有看过react源码,所以这里只是简单的阐述从虚拟DOM变成DOM的流程。
我们在实现一个React组件时可以选择两种编码方式,第一种是使用JSX编写:
class Hello extends Component {
render() {
return <div>Hello ConardLi</div>;
}
}
第二种是直接使用React.createElement编写:
class Hello extends Component {
render() {
return React.createElement('div', null, `Hello ConardLi`);
}
}
实际上,上面两种写法是等价的,JSX只是为 React.createElement(component, props, ...children)方法提供的语法糖。也就是说所有的JSX代码最后都会转换成React.createElement(...),Babel帮助我们完成了这个转换的过程。 如下面的JSX
<div>
<img src="avatar.png" className="profile" />
<Hello />
</div>;
将会被Babel转换为
React.createElement("div", null, React.createElement("img", {
src: "avatar.png",
className: "profile"
}), React.createElement(Hello, null));
注意,babel在编译时会判断JSX中组件的首字母,当首字母为小写时,其被认定为原生DOM标签,createElement的第一个变量被编译为字符串;当首字母为大写时,其被认定为自定义组件,createElement的第一个变量被编译为对象;
下面我们来看看虚拟DOM的真实模样,将下面的JSX代码在控制台打印出来:
<div className="title">
<span>Hello ConardLi</span>
<ul>
<li>苹果</li>
<li>橘子</li>
</ul>
</div>
而ReactElement最终形成的树结构就是Virtual DOM; 整体的转换过程如下: