前端性能优化 css和js的加载与执行
一个网站在浏览器端是如何进行渲染的?
html本身首先会被渲染成 DOM 树,实际上 html 是最先通过网址请求过来的,请求过来之后,html 本身会由一个字节流转化成一个字符流,浏览器端拿的就是字符流,然后通过词法分析之后,将相应的语法分析成相应的 token ,比如说 header token, 转化不同的 token tag ,然后通过 token 类型 append 到 dom 树。
遇到 link token tag,然后去请求 css ,请求过来之后再去对 css 进行解析,生成 CSSOM ,然后和 DOM 树进行结合形成 Render Tree 这样的渲染树。然后再进行布局和渲染。
遇到 script tag ,然后去请求 JS 相关的 web 资源,请求回来的 js 会交给浏览器的 v8 引擎进行解析,
HTML渲染过程的一些特点
1、顺序执行
因为他是使用词法分析的能力,从上到下依次分析整个html,tag 相应的情况,所以第一个特点是顺序执行。词法分析是对 html 这个文档解析的一个方式,对 tag 依次从上到下解析,这个从上到下决定了很多阻塞的情况。
2、并发加载
我们的html中可能会引入很多的 css,js 的 web 资源,这些 web 资源在浏览器端是并发加载的,这里需要优化的一点就是这个并发加载过程的并发度是受我们浏览器域名限制的。所以会设置 3 到 4 个 CDN 域名,防止单个域名的限制
3、是否阻塞
首先 css 的加载是否会阻塞 js 的加载,js 的加载是否阻塞 js 的执行, css 的加载是否会阻塞页面的渲染。
css 在 head 中通过 link 方式引入的话,他会阻塞页面的渲染。就是要等这个 css 加载完之后才会进行渲染。所以这个时候分析完之后是带样式的,所以推荐是放在 head 里面请求 css
css 通过 link 的方式,css会阻塞页面的渲染,css 会阻塞 js 的执行,因为 js 会影响样式,js 的执行会依赖一些 css 的属性,如果 css 没有加载完,可能会有问题。但 css 不阻塞外部脚本的加载,原因是 webkit 实际上有一个预先扫描器,他是可以预先扫描后面的词语的,所以他不会阻止加载的过程,但会阻止执行的过程。
直接引入的js会阻塞页面的渲染,因为 js 的执行会影响 dom 节点的修改,所以会阻塞后面的节点创建。这也是符合逻辑的。
4、依赖关系
html渲染过程中,是否有要遵循的依赖关系,如何保证依赖关系正确的情况下提高效率。比如有些情况 html 已经出来了,但是样式没有,然后突然闪了一下,样式出来了。这种情况就是没有遵循依赖关系,一般 css 放在 head tag 里面,页面就会等 css 加载好,生成 cssom ,再进行渲染,这个时候就不会出现 css 闪动的问题。有时候 js 会加 async,这个时候就是放弃的 js 的依赖关系,这时候需要关注js的依赖关系
页面渲染依赖于 css 的加载
js 的执行顺序的依赖关系
js 逻辑对于 dom 节点的依赖关系,有些 js 需要去获取 dom 节点
5、引入方式
比如是 link 引入,还是 import 引入,两者有什么区别。对于js,比如 tag,比如动态引入。
直接引入,会阻塞页面的渲染
defer,不会阻塞页面的渲染,在 defer 的时候,所有 dom 树都已经构建完成了。defer 是顺序执行
async,不会阻塞页面的渲染,跟 defer 不同的是,不保证执行顺序,哪个先从服务端回来,哪个先执行
异步动态引入js,需要的时候,动态引入新的js文件,spa 单页页面中非常广泛
根据这些特点,我们找到可以优化的点
1、css 样式表置顶
2、用 link 代替 import,指的是 css 里面的 import,这个 import 是写在最底层,这样引入 css 的时候是不好的
3、js 脚本置底,并发请求的时候不区分 css 和 js 的,js 写在顶部会影响 css 的加载,在渲染的时候,css 是由于 js的,所以跟页面渲染无关的 js 放于页面底部
4、合理使用 js 的异步加载能力,优化js的加载执行
实战
1、加载并发数是有上限的,js和css混合放置,会导致css的延迟,会导致页面闪动,所以js要置底
2、css放在header中,阻塞页面的渲染,css加载完,再加载dom,放置页面样式跳变,从而保证渲染一步到位
3、css不会阻塞后面js并发加载,但会阻塞js的执行。如果js放在header中,会阻塞html的渲染。
4、把js都放在header, 分别放 async,defer。
async 不阻塞页面渲染,不保证执行顺序,谁先回来谁执行,不保证dom书构造完成之后再执行。
defer 不阻塞页面渲染, 保证执行顺序,保证dom树构造完成之后再执行。
5、@import link 实操
@import 有两个重大缺陷,第一点它不支持并发执行。他要 import 之后,再去 import 第二个,他不支持并发。
第二点,他需要整个页面全部加载完之后,才去执行 import 里面的代码。
但是,,,可能这两个重大缺陷曾经存在过,但是现在这两个问题已经没有了,跟 link 的效果是一模一样的。他更适合用于 css 模块化。但是在不同 css 文件中 @import 的是不支持并发的,只有该 css 文件加载完之后才去加载 @import 里面的 css