浏览器渲染流程
解析HTML生成DOM树
解析CSS生成CSSOM树
将DOM树跟CSSOM树合成渲染树
遍历渲染树开始布局,计算每个节点的位置大小信息(重排发生之处)
绘制节点到屏幕(重绘)
js操作真实DOM的代价
虽然DOM是由JavaScript实现的,但是在浏览器中,DOM与JavaScript是分开来实现的。所以每一次通过js来操作DOM,都需要去链接js跟DOM。可以这样设想,js跟DOM是两个独立的城市,每一次操作,都是由js到达DOM,代价就是中间的路费,还有操作的费用。而越是频繁操作,代价越大。比如,有10个js更新DOM的操作,当第一个DOM请求操作发生的时候,浏览器不知道后面还有9次请求操作,DOM会马上走一个渲染流程。接着下一个请求操作,这个节点已经发生了改变,前一次的计算是无用功了,计算DOM节点的属性等都是白白浪费性能。
虚拟DOM
- 构建虚拟DOM树
- 元素发生更改,生成一颗新的DOM树,对比旧的DOM树,找到差异点
- 把差异点应用到真实DOM上去,实现局部替换
为什么需要虚拟DOM
可以解决js频繁操作DOM产生的性能问题。因为DOM的多次的操作不会立即操作DOM,而是会先反应到js对象(新的DOM树)上,通过比较,找到差异点,然后将差异的地方应用到真实DOM上去渲染,然后更新内存的DOM树(对象)。这样就避免了大量的无谓的过时的计算。
实现虚拟DOM
function virtualDOM (tag, props, children) {
if (!this instanceof virtualDOM) {
return new virtualDOM(tag, props, children)
}
this.tag = tag;
this.props = props || {};
this.children = children || [];
this.key = props ? props.key : undefined;
let count = 0;
this.children.forEach((child) => {
if (child instanceof virtualDOM) {
count += child.count;
}
count++;
});
this.count = count;
}
virtualDOM.prototype.render = function() {
const el = document.createElement(this.tag);
const props = this.props;
for (const prop in props) {
el.setAttribute(prop, props[prop])
}
this.children.forEach((child) => {
const childEle = (child instanceof virtualDOM) ?
child.render : document.createElement(child.tag);
el.appendChild(childEle);
})
return el;
}