Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么css要放头部,js放尾部以及async、defer该如何处理? #23

Open
Yuanfang-fe opened this issue Apr 23, 2021 · 0 comments

Comments

@Yuanfang-fe
Copy link
Owner

css要放头部,js放尾部 主要是为了将页面内容尽快的展示给用户,减少白屏时间。

接下来我们来分析下原因,但是在这之前要先清楚浏览器页面的渲染过程。

  1. DOM的解析是一个从上到下的过程
  2. 所有外链资源(css,image,script)浏览器都会以接近并发的情况来�发起请求(http 1.x 同域名下不是并发,详情看这里
  3. 同一时间内的最大HTTP请求是有上限的(http 1.1浏览器会限制同一个域的同时请求数,Chrome是限制6个,总连接数是17个)
  4. 解析DOM获取DOM树,解析CSS获取CSSDOM树, 两者�合成渲染树(解析与构建是同步进行的,渲染树内部是拥有布局的,以及盒子模型的)
  5. 获取到渲染树之后将会进行绘制Paint,进行像素级别的渲染,绘制到屏幕上。
  6. js会堵塞DOM树的解析
  7. 当JS面前有一个link css ,无论两者�谁先�下载完毕,JS都会等待CSS加载并解析完成后再执行。这是因为浏览器不知道JS是否会需要查询CSSDOM,所以需要等待CSS准备完毕
  8. 渲染必须依赖CSSDOM树,可以认为CSS是堵塞渲染,但是不堵塞DOM解析

关于渲染过程更细节的内容,点击这里 从输入URL到页面渲染出来的过程

css放头部

所以,css 如果放在标签的前面,那么当DOM树构建完成了,渲染树才构建,那么当渲染树构建完成,浏览器不得不再重新渲染整个页面,这样不仅造成了资源的浪费,效率也不高。

对用户来讲,页面重新渲染的时候也会看到页面闪动,用户体验可谓极差。

如果放在之间,浏览器边构建边渲染,效率要高的多。

js放尾部

当浏览器解析到script的时候,就会立即下载执行,中断html的解析过程,如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。

具体的流程是这样的:

  1. 浏览器一边下载HTML网页,一边开始解析。
  2. 解析过程中,发现script标签
  3. 暂停解析,网页渲染的控制权转交给JavaScript引擎
  4. 如果script标签引用了外部脚本,就下载该脚本,否则就直接执行
  5. 执行完毕,控制权交还渲染引擎,恢复往下解析HTML网页

外链的script包含async或者defer如何处理?

这两个属性只是script标签在header标签中使用的,如果你把它放在body后面是无效的。

script 的这两个属性主要用于其js文件没有操作DOM的情况,这时候就可以将该js脚本设置为异步加载,通过async或defer来标记代码

async和defer的区别:

  1. async和defer都仅对外部脚本有效,对于内置而不是连接外部脚本的script标签,以及动态生成的script标签不起作用。
  2. async和defer虽然都是异步的,不过使用async标志的脚本文件一旦加载完成就会立即执行;而使用defer标记的脚本文件,会在 DOMContentLoaded 事件之前(也就是页面DOM加载完成时)执行。
  3. 如果有多个js脚本文件,async标记不保证按照书写的顺序执行,哪个脚本先下载结束,就先执行那个脚本。而defer标记则会按照js脚本书写顺序执行。
  4. 一般来说,如果脚本之间没有依赖关系,就使用async属性,如果脚本之间有依赖关系,就使用defer属性。如果同时使用async和defer属性,后者不起作用,浏览器行为由async属性决定。

对于async标记,浏览器的解析过程是这样的:

  • 浏览器开始解析HTML网页
  • 解析过程中,发现带有async属性的script标签
  • 浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本
  • 脚本下载完成,浏览器暂停解析HTML网页,开始执行下载的脚本
  • 脚本执行完毕,浏览器恢复解析HTML网页

对于defer标记,浏览器的解析过程是这样的:

  • 浏览器开始解析HTML网页
  • 解析过程中,发现带有defer属性的script标签
  • 浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本
  • 浏览器完成解析HTML网页,此时再执行下载的脚本

由于使用了async或defer的script会放在header中,而header又会存在外链css,那么二者有顺序要求吗?

因为 js 的执行是依赖 css 样式 的。即只有 css 样式全部下载完成后才会执行js。

由于现代浏览器很聪明会进行 prefetch 优化,就如 Chrome 浏览器,它会在解析 HTML 时收集外链,并在后台并行下载,由于会并行下载,那么head中外链js和css的位置其实就没有什么很大影响了。

参考:
https://juejin.cn/post/6844904009694707725#heading-2
https://www.zhihu.com/question/23250329
https://www.cnblogs.com/huaweiyun/p/13202745.html
https://yinode.tech/post/201901/%E4%BB%8Ejs%E7%9A%84%E9%98%BB%E5%A1%9E%E8%A7%92%E5%BA%A6%E8%B0%88%E8%B0%88%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86/#%E6%83%85%E5%86%B52-css%E6%94%BE%E5%A4%B4%E9%83%A8-js%E6%94%BE-body-%E5%89%8D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant