简介
本文介绍浏览器的页面的渲染阻塞的场景及其优化方案。
CSS和JavaScript都会阻塞浏览器的渲染,本文分这两个方面介绍。
CSS阻塞渲染
- CSS不会阻塞DOM的解析。
- 原因:HTML的DOM的解析与CSS解析是独立进行的,所以CSS解析不会阻塞DOM的解析。
- CSS可能会阻塞渲染。
- 不同的CSS的引入方式结果不同。
阻塞的场景
项 | style标签中的样式 | link引入的样式 |
解析器 | 由HTML解析器解析 | 由CSS解析器解析 |
是否阻塞DOM解析 | 否 | 否 |
是否阻塞渲染 | 不阻塞浏览器渲染(可能会产生“闪屏现象”) | 阻塞浏览器渲染(可以利用这种阻塞避免“闪屏现象”) |
是否阻塞JS | 是 | 是 |
为什么不阻塞渲染会引起闪屏呢?
因为不阻塞渲染就导致CSS很可能在渲染结束后生效,若渲染结束时CSS和后边新生效的CSS不同,那很显然会改变显示效果。
优化方案
CSS阻塞优化方法:尽可能快的提高外部css加载速度
- 使用CDN节点进行外部资源加速。
- 对css进行压缩(利用打包工具,比如webpack,gulp等,也可以通过开启gzip压缩)。
- 减少http请求数,将多个css文件合并或者是干脆直接写成内联样式(内联样式的一个缺点就是不能缓存)。
- 优化样式表的代码
JavaScript阻塞渲染
阻塞的场景
JavaScript会阻塞DOM解析、阻塞页面渲染、阻塞后续JS的执行。
1. JS阻塞DOM解析
浏览器不知道后续脚本的内容,如果先去解析了下面的DOM,而随后的JS删除了后面所有的DOM,那么浏览器就做了无用功,浏览器无法预估脚本里面具体做了什么操作,例如像document.write 这种操作,索性全部停住,等脚本执行完了,浏览器再继续向下解析DOM。
2. JS阻塞页面渲染
JS中也可以给DOM设置样式,浏览器同样等该脚本执行完毕,再继续干活,避免做无用功。
3. JS阻塞后续js的执行
为了维护依赖关系,例如:必须先引入jQuery再引入bootstrap
优化方案
因为JS会阻塞后续的HTML解析、渲染,所以对JS的阻塞的优化方案大致就是:将JS放到最后、异步执行。
方案1:将JS放到最后
很简单,将script标签放到HTML文件的body标签的最后即可。例如:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is title</title>
</head>
<body>
<div class="container">
这是个Demo
</div>
<script>
let name = 'hello'
</script>
</body>
</html>
方案2:defer延迟加载
<script src="app1.js" defer> </script>
defer 属性表示延迟执行引入 JavaScript,即 JavaScript 加载时 HTML 不会停止解析,这两个过程是并行的。 整个HTML解析完毕且defer-script也加载完成之后,才会执行所有由defer-script加载的JavaScript代码,再触发 DOMContentLoaded事件。
defer 不会改变 script 中代码的执行顺序。
所以,defer 与相比普通 script,有两点区别:
- 载入 JavaScript 文件时不阻塞 HTML 的解析。
- 执行阶段被放到 HTML 的解析完成之后。
方案3:async异步加载
async异步加载:告诉浏览器不必等到加载完外部文件,可以边渲染边下载,什么时候下载完成什么时候执行。
写法1:标签指定
<script src="app1.js" async> </script>
写法2:用js动态创建script元素
<script type="text/javascript">
(function () {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true; //这句可以删除,默认就是true。
s.src = 'js/a.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
</script>
这种方法会阻塞onload事件
写法3: onload事件触发时异步加载
等html的文件,图片之类的、页面所有的资源全部加载完成后再下载执行js,这样的方法可以避免阻塞onload事件的触发。
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'js/yibujiaz.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent){
window.attachEvent('onload', async_load);
} else {
window.addEventListener('load', async_load, false);
}
})();
补充内容
CSS与JS
CSS的解析和JS的执行是互斥的(互相排斥),CSS解析的时候JS停止执行,JS执行的时候CSS停止解析。
不会阻塞外部资源
CSS阻塞和JS阻塞都不会阻塞浏览器加载外部资源(图片、视频、样式、脚本等)。
原因:浏览器始终处于一种:“先把请求发出去” 的工作模式,只要是涉及到网络请求的内容,无论是:图片、样式、脚本,都会先发送请求去获取资源,至于资源到本地之后什么时候用,由浏览器自己协调。这种做法效率很高。
预解析
WebKit 和 Firefox 都进行了预解析这项优化。在执行js脚本时,浏览器的其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树。