一、客户端从服务器获取到需要渲染页面的源代码后,「开辟一个“GUI渲染线程”,自上而下解析代码,最后绘制出对应的页面」;自上而下渲染解析代码的过程是“同步”的,但是有些操作也是异步的;
二、 页面渲染的步骤:
三、前端性能优化 「CRP:关键渲染路径」
1、生成DOM TREE
+ 减少DOM的层级嵌套
+ 不要使用“非标准”的标签
2、生成CSSOM
-
尽可能不要使用@import(阻塞GUI渲染)
-
如果CSS代码比较少,尽可能使用“style内嵌样式”(尤其是移动端开发)
-
如果使用link,尽可能把所有的样式资源合并为一个,且压缩(减少HTTP请求数量,因为HTTP的并发性也是有限制的,以及渲染CSS的时候,也不需要再计算依赖关系...)
-
CSS选择器链短一些(因为CSS选择器渲染是从右到左的)
-
把link等导入CSS的操作放在HEAD中(目的是:一加载页面就开始请求资源,同时GUI去生成DOM树 “CSS等资源预先加载”)
3、 对于其他资源的优化
-
对于script,尽可能放置在页面的底部(防止其阻塞GUI的渲染);
-
对于部分script需要使用async或者defer;
-
async是不管JS的依赖关系的,哪一个资源先获取到,就先把这个资源代码渲染执行
-
defer不会这样的,和link一样,是等待所有script defer都请求回来后,按照导入顺序/依赖关系依次渲染执行的
-
懒加载:第一次加载页面的时候不要加载请求图片,哪怕它是异步的,但是也占据了HTTP并发的数量,导致其他资源延后加载
-
图片的BASE64:不用去请求加载图片,BASE64码基本上代表的就是图片,而且页面渲染图片的时候速度也会很快(慎用,但是在webpack工程化中可以使用,因为它基于file-loader可以自动base64)
4、Layout/Painting:重要的优化手段(减少DOM的“回流/重排”和重绘)
- 第一次加载页面必然会有一次回流和重绘
- 触发回流操作后,也必然绘触发重绘;如果只是单纯的重绘,则不会引发回流;性能优化点,重点都在回流上;
5、 操作DOM消耗性能? =>DOM的回流
- 元素在视口中的大小或者位置发生变化
- 元素的删除或者新增(以及基于display控制显示隐藏)
- 浏览器视口大小发生改变
- ...
这些操作都需要浏览器重新计算每一个元素在视口中的位置和大小(也就是重新Layout/Reflow)
6、 当代浏览的渲染队列机制:在当前上下文操作中,遇到一行修改样式的代码,并没有立即通知浏览器渲染,而是把其放置在渲染队列中,接下来看是否还有修改样式的代码,如果有继续放置在渲染队列中...一直到再也没有修改样式的代码或者“遇到一行获取样式的操作”,这样都会刷新浏览器的渲染队列机制(也就是把现在队列中修改样式的操作,统一告诉浏览器渲染,这样只会引发一次回流)
box.style.width = "100px"; box.style.height = "200px"; box.offsetHeight; //box.style.xxx 或者 window.getComputedStyle(box).xxx 再 或者 box.clientWidth|Height|Top|Left 以及 box.offsetWidth|Height|Top|Left 或者 box.scrollWidth|Height|Top|Left ...这些获取样式的操作都会刷新渲染队列
box.style.position = "absolute";
box.style.top = "100px";
7、总方式:不要自己直接去操作DOM,例如vue/react
(1).样式的“分离读写”:把修改样式和获取样式代码分离开
box.style.width = "100px";
box.style.height = "200px";
box.style.position = "absolute";
box.style.top = "100px";
box.offsetHeight;
(2)利用某种方式一次性设置样式
box.style.cssText = "width:100px;height:200px;...";
box.className = ".boxActive";
(3)这种方式不好(应为把获取操作和设置混合了)
box.style.width = box.offsetWidth + 10 + 'px';
box.style.height = box.offsetHeight + 10 + 'px';
应该改为:
let w = box.offsetWidth,
h = box.offsetHeight;
box.style.width = w + 10 + 'px';
box.style.height = h + 'px';
8、新增元素
let arr = ["张三", "李四", "王五"];
循环几次引发几次回流
arr.forEach(item => {
let span = document.createElement('span');
span.innerText = item;
document.body.appendChild(span);
});
引发了三次回流
处理方式:
(1)模板字符串:可能因为把原始容器的内容变为字符串和新的字符串拼接,最后再整体渲染回去,导致原始容器中的元素绑定的一些事件失效...
let str = ``;
arr.forEach(item => {
str += `<span>
${item}
</span>`;
});
document.body.innerHTML += str;
(2) 文档碎片:临时存放元素对象的容器
let frag = document.createDocumentFragment();
arr.forEach(item => {
let span = document.createElement('span');
span.innerText = item;
frag.appendChild(span);
});
document.body.appendChild(frag);
frag = null;
9、把动画等频发样式改变的操纵,运用到position:fixed/absolute...上 「脱离文档流:单独一层」
- 利用分层机制,如果只改变一个层面上的位置大小等信息,浏览器回流和重绘的速度会加快很多
10、修改元素的 transform / opacity(filters)... 的这些样式,不会引发DOM的回流 「浏览器的硬件加速,弊端就是消耗浏览器的内存」
11、修改样式的时候,巧妙的利用获取样式的操作,触发回流解决一些bug
setTimeout(() => {
// 立即回到left:0的位置
box.style.transitionDuration = '0s';
box.style.left = 0;
// 刷新渲染队列(会增加一次回流)
box.offsetLeft;
// 回到开始位置后,再次运动到left:200位置(有动画)
box.style.transitionDuration = '0.5s';
box.style.left = '200px';
}, 1000);
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!