<script>
标签用于在HTML
中引入js
,本文主要讨论三方面:
- script标签在不同位置引入的区别(如在head引入和在body引入,在body开头与最后引入)
- script标签的三种引入方式(default,async,defer),
- script标签模块化(type='module')
加载与执行的区别
脚本的加载(Loading)与执行(Execution)是两个独立的概念,它们的行为和影响因素不同,理解这一点对我们后面的讨论至关重要.
- 加载: 浏览器从服务器获取脚本文件(如
script.js
)的过程,涉及网络请求和资源下载。 - 执行: 浏览器解析并运行脚本中的 JavaScript 代码。
引入位置区别
<head>
中引入或在<body>
最开始引入
-
浏览器会暂停 HTML 解析,立即加载并执行脚本,执行完成后才继续解析 DOM。
-
若脚本体积大或网络慢,会导致页面渲染阻塞(白屏时间长)。
-
如果脚本尝试操作 DOM(如
document.getElementById
),可能因为 DOM 还未解析而找不到元素,导致错误。 -
适用场景
- 需要尽早执行 且不依赖 DOM 的脚本(如某些性能监控、日志上报代码)。
- 一般不推荐,除非有特殊需求。
<body>
底部引入:
-
不阻塞 DOM 解析 ,浏览器会先解析完整的 HTML,最后再加载和执行脚本,不会影响页面渲染速度。用户能更快看到页面内容(减少 FCP,First Contentful Paint)。
-
脚本执行时 DOM 已就绪 ,脚本运行时,整个 DOM 已经解析完成,可以安全操作 DOM 元素,无需等待
DOMContentLoaded
事件。 -
适用场景
- 推荐的经典做法,适用于大多数情况,特别是依赖 DOM 的脚本(如交互逻辑、初始化组件)。
- 如果脚本较大,仍可能影响
DOMContentLoaded
事件触发时间。
三种引入
默认引入
遵循多个脚本按书写顺序依次加载并执行的原则。
defer
html
<script src="script.js" defer></script>
-
行为:
- 脚本异步加载 (不阻塞 HTML 解析),但会延迟到 DOM 解析完成后、
DOMContentLoaded
事件前按顺序执行。 - 适用于需要操作 DOM 或依赖其他脚本的场景。
- 脚本异步加载 (不阻塞 HTML 解析),但会延迟到 DOM 解析完成后、
-
特点:
- 保证脚本的执行顺序(与书写顺序一致)。
- 天然支持"等 DOM 就绪",类似
DOMContentLoaded
。
async
html
<script src="script.js" async></script>
-
行为:
- 脚本异步加载 (不阻塞 HTML 解析),加载完成后立即执行(可能中断 HTML 解析)。
- 适用于独立脚本(如统计代码、第三方 SDK)。
-
特点:
- 不保证执行顺序(谁先加载完谁先执行)。
- 不适合操作 DOM 的脚本(可能 DOM 还未解析完)。
模块化type="module"
html
<script type="module" src="module.js"></script>
-
场景:
-
你的代码需要按模块化拆分,通过
import
引入其他模块或通过export
暴露功能。html<script type="module"> import { utils } from './utils.js'; import Component from './Component.js'; </script>
-
-
行为:
- 避免全局变量污染,依赖关系清晰,适合大型项目。
- 默认具有
defer
行为(异步加载,DOM 解析后执行)。 - 若显式添加
async
(如<script type="module" async>
),则行为类似async
。
-
特点:
- 支持 ES6 模块语法(
import
/export
)。 - 自动启用严格模式。
- 模块脚本默认延迟执行 ,且按依赖关系顺序执行。
- 支持 ES6 模块语法(