页面和资源加载
页面生命周期
页面的生命周期重要的就这四个:
- DOMContentLoaded
浏览器完全加载 HTML,构建了 DOM 树,但是 img,stylesheet 等外部样式不确定INFO
浏览器
自动填充表单功能
会在这里触发 - load
浏览器完成了所有资源的加载 - beforeUnload
用户正在离开页面,此时可以询问是否保存了重要内容 - unload
用户几乎已经离开,此时可以发送统计数据等内容INFO
浏览器的
navigator.sendBeacon
方法可以在后台发送数据,保证页面离开流畅
脚本加载
对于普通 <script>
资源标签来讲,如果遇到它就会停止构建 DOM 然后执行脚本内容, 所以上述的事件 DOMContentLoaded
是发生在所有普通 script
标签加载之后的
然而万事万物都有例外:
- 带有
async
属性的脚本不会阻塞DOMContentLoaded
- 通过
document.createElement('script')
创建的脚本不会阻塞DOMContentLoaded
样式表不会直接阻塞 DOMContentLoaded
但是会阻塞它后面的 script
, 这样会间接地阻塞 DOMContentLoaded
,比如:
<link rel="stylesheet" href="style.css"></link>
<script>
// 可能要获取上面样式的属性,所以就等待
</script>
加载进度、状态
document.readyState
表示文档加载状态,它的值有三种:
loading
,正在加载interactive
——DOMContentLoaded
文档读取完成complete
——load
所有资源加载完成
TIP
上述列表后两条是几乎同时发生的,但是 document.readyState 的变化在先, window 对应的变化(生命周期)在后
特殊属性 async、defer
如果 <script>
标签添加了上述属性,则会产生不同的 “效果”
相同点:
脚本的加载不会阻塞页面渲染(并行加载)
仅对外部资源有效(带 src
属性的)
不同点:
- defer 按照
<script>
标签顺序加载,即使 bundle2 率先加载完成也会等等 bundle1 - DOM 构建完成后,脚本才会执行
- defer 加载会在事件
DOMContentLoaded
之前完成
<script src="bundle1.min.js" defer></script>
<script src="bundle2.min.js" defer></script>
TIP
适合跟 DOM 无关的脚本
而 async 则表现的格外 “独立”,所有带 async 的 <script>
标签的执行顺序表现为:
- 先加载完成的先执行
- 脱离生命周期的 “限制”,与
DOMContentLoaded
无关
TIP
适合广告、统计等脚本
提上这么一嘴
下面的脚本只会输出 hello 而没有 world, 知识点不言而喻了吧。
<script src="index.js">
console.log('world')
</script>
console.log('hello')
动态脚本
通过 document.createElement('script')
创建的脚本默认会异步执行,行为跟 “async” 一样
但是可以显式地设置 script.async = false
使得它的行为变成 “defer”
模块(module)
这样的脚本 <script src="xxx" type="module"></script>
称之为模块,这里不讨论模块,只讨论 async 和 defer
属性
模块 默认 总是延迟加载的(不阻塞页面渲染),表现为 defer 的特性
async
也 可以适用于内联脚本js<script type="module" async> import {config} from './nanote.js' </script>