脚本加载与文档模式
script 标签有哪两种写法?
<!-- 外部脚本; src 与行内代码同时存在时忽略行内代码 -->
<script src="example.js" defer></script>
<!-- 行内脚本 -->
<script>
console.log(1);
</script>
- 执行顺序: 未加
async/defer的脚本按出现顺序依次执行, 并阻塞 HTML 解析;
async 与 defer 如何改变脚本执行时机?
| 属性 | 是否阻塞解析 | 执行时机 | 执行顺序 |
|---|---|---|---|
| 默认 | 阻塞 | 下载完成即执行 | 按标签顺序 |
async | 不阻塞 | 下载完成后立刻执行, 不等 HTML 解析完成 | 不保证顺序 |
defer | 不阻塞 | HTML 解析完成后、DOMContentLoaded 前执行 | 按标签顺序 |
- 适用范围:
async与defer仅对外部脚本生效, 标签没有src时两个属性都被忽略, 行内脚本照样阻塞解析; - 完成时机:
async脚本在load事件前执行完毕,defer脚本在DOMContentLoaded事件前执行完毕;
defer 与 async 的顺序保证有什么差别?
- 下载: 浏览器会扫描页面并行下载脚本, 所以后出现的
small.js常常先下载完; defer: 仍按文档顺序执行, 先下完的small.js必须等long.js执行完才运行, 适合"先加载库、再加载依赖它的脚本";async: 谁先加载完谁先执行 (load-first 顺序), 文档顺序完全不参与, 适合计数器、广告等相互独立的第三方脚本;
<script defer src="long.js"></script>
<script defer src="small.js"></script>
<!-- 即使先下载完, 也排在 long.js 之后 -->
<script async src="long.js"></script>
<script async src="small.js"></script>
<!-- 谁先加载完谁先执行 -->
- 与
DOMContentLoaded的关系:defer脚本保证在该事件前执行完,async脚本与该事件互不等待 (可能在其前也可能在其后), 详见 页面加载事件;
动态插入的 script 按什么顺序执行?
function loadScript(src) {
const script = document.createElement("script");
script.src = src;
script.async = false; // 关键: 强制按插入顺序执行
document.body.append(script); // 追加到文档后立即开始下载
}
loadScript("long.js"); // 一定先执行
loadScript("small.js");
- 默认行为: 动态脚本等价于
async, 不等别人也不被别人等, 先加载完的先执行; script.async = false: 让它按插入顺序执行, 效果与defer相同;- 用途: 库脚本与依赖它的脚本必须保持顺序时使用;
- 时机: 脚本在插入文档的那一刻开始下载, 不必等 HTML 解析完;
async 与 defer 有哪些使用限制?
- 好处: 下载过程不阻塞渲染, 用户能立刻看到页面内容;
- 代价: 页面先可见、组件后初始化, 未就绪的按钮应禁用并给出加载提示;
- 为什么需要它们: 脚本可能改 DOM 甚至
document.write, 浏览器不敢边解析边执行, 只能停下来等脚本; - 退路方案: 把脚本放到
</body>前也能避免挡住页面, 但浏览器要等整份 HTML 下载完才认识它并开始下载, 长文档下延迟明显;
noscript 什么时候显示?
- 位置: 放在
body中; - 显示条件: 浏览器不支持或禁用了 JavaScript 时显示其内容;
<body>
<noscript>
<p>This page will be displayed in a JavaScript-unable browser.</p>
</noscript>
</body>
文档模式有哪些, 代码如何检测?
- quirks mode: 无文档声明或旧声明时的兼容模式;
- standards mode: 推荐, 由标准
<!DOCTYPE html>触发; - almost standards mode: 行为接近标准模式;
- 检测方式: 读取
document.compatMode,CSS1Compat表示标准模式,BackCompat表示怪异模式;
if (document.compatMode === "CSS1Compat") {
console.log("Standards mode");
} else {
console.log("Quirks mode");
}