html,css,js加载顺序
1.js 放在 head 中会立即执行,阻塞后续的资源下载与执行。因为 js 有可能会修改 dom,如果不阻塞后续的资源下载,dom 的操作顺序不可控。
正常的网页加载流程是这样的。
- 浏览器一边下载 HTML 网页,一边开始解析
- 解析过程中,发现
<script>
标签 - 暂停解析,网页渲染的控制权转交给 JavaScript 引擎
- 如果
<script>
标签引用了外部脚本,就下载该脚本,否则就直接执行 - 执行完毕,控制权交还渲染引擎,恢复往下解析 HTML 网页
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。html 需要等 head 中所有的 js 和 css 加载完成后才会开始绘制,但是 html 不需要等待放在 body 最后的 js 下载执行就会开始绘制, 因此将 js 放在 body 的最后面,可以避免资源阻塞,同时使静态的 html 页面迅速显示。将脚本文件都放在网页尾部加载,还有一个好处。在 DOM 结构生成之前就调用 DOM,JavaScript 会报错,如果脚本都在网页尾部加载,就不存在这个问题,因为这时 DOM 肯定已经生成了。
2.js 的执行依赖前面的样式。即只有前面的样式全部下载完成后才会执行 js,但是此时外链 css 和外链 js 是并行下载的。
css 需要分块,首页的 css 独立,其余的 css 需要动态加载,因为 html 的绘制会被 css 阻塞,这样可以减少首次进入时的白屏时间。
3. 外链的 js 如果含有 defer="true" 属性,将会并行加载 js,到页面全部加载完成后才会执行,会按顺序执行。
defer
属性的作用是,告诉浏览器,等到 DOM 加载完成后,再执行指定脚本。
- 浏览器开始解析 HTML 网页
- 解析过程中,发现带有
defer
属性的 script 标签 - 浏览器继续往下解析 HTML 网页,同时并行下载 script 标签中的外部脚本
- 浏览器完成解析 HTML 网页,此时再执行下载的脚本
对于内置而不是连接外部脚本的 script 标签,以及动态生成的 script 标签,defer
属性不起作用。
4. 外链的 js 如果含有 async="true" 属性,将不会依赖于任何 js 和 css 的执行,此 js 下载完成后立刻执行,不保证按照书写的顺序执行。因为 async="true" 属性会告诉浏览器,js 不会修改 dom 和样式,故不必依赖其它的 js 和 css。
async
属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染。
- 浏览器开始解析 HTML 网页
- 解析过程中,发现带有
async
属性的script
标签 - 浏览器继续往下解析 HTML 网页,同时并行下载
script
标签中的外部脚本 - 脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本
- 脚本执行完毕,浏览器恢复解析 HTML 网页
async
属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。另外,使用async
属性的脚本文件中,不应该使用document.write
方法。
一般来说,如果脚本之间没有依赖关系,就使用async
属性,如果脚本之间有依赖关系,就使用defer
属性。如果同时使用async
和defer
属性,后者不起作用,浏览器行为由async
属性决定。