跳到主要内容

脚本加载与文档模式

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");
}