关于<script>的静态加载策略
- 无 defer 或 async 的 <script>(默认行为)
- 加载:同步(阻塞式)下载,暂停 HTML 解析(即阻塞 DOM 构建和渲染)。
- 执行:脚本下载完成后立即执行,执行期间继续阻塞渲染,执行完毕后才恢复解析。
- async 属性
- 加载:异步下载,不阻塞 HTML 解析(与 DOM 构建并行)。
- 执行:脚本下载完成后立即执行(无论 DOM 是否解析完成),执行期间可能短暂阻塞渲染。
- 顺序:多个 async 脚本的执行顺序不可预测(取决于下载完成顺序)。
- defer 属性
- 加载:异步下载,不阻塞 HTML 解析(与 DOM 构建并行)。
- 执行:脚本会在 DOM 解析完成(DOMContentLoaded 事件触发前) 按文档顺序执行。
- 顺序:多个 defer 脚本严格遵循在 HTML 中的声明顺序执行。
关于<script>的动态生成与应用场景
除此之外,script还可以动态生成,适用于以下应用场景:
-
延迟加载,减少首屏阻塞;
// 在 DOM 解析完成后加载脚本(类似 defer) document.addEventListener('DOMContentLoaded', () => { const script = document.createElement('script'); script.src = 'non-critical.js'; // 非关键脚本 document.body.appendChild(script); });
-
用户触发特定行为(如点击按钮、滚动到某位置)后再加载脚本;
document.getElementById('loadButton').addEventListener('click', () => { const script = document.createElement('script'); script.src = 'component.js'; document.head.appendChild(script); });
-
脚本 URL 需根据运行时条件(如用户权限、设备类型、用户语言等)动态生成;
// 检查权限后加载对应脚本 const userRole = 'admin'; // 实际从cookie/localStorage获取 const script = document.createElement('script'); script.src = userRole === 'admin' ? 'https://cdn.example/admin-sdk.js' : 'https://cdn.example/user-sdk.js'; document.head.appendChild(script);
-
脚本加载失败时,动态回退到备用 CDN等应用场景;
const script = document.createElement('script'); script.src = 'https://unstable-cdn.com/library.js'; script.onerror = () => { const fallback = document.createElement('script'); fallback.src = '/local/library.js'; document.head.appendChild(fallback); }; document.head.appendChild(script);
-
JSONP跨域请求(获取远程数据)。
动态生成 <script> 标签并设置 src 的方式可以绕过 CORS 限制(而在脚本内通过 fetch/XHR 发起的 HTTP 请求会受CORS限制),但只能用于 JSONP这种特定场景,而不是通用的跨域请求方法。
function handleData(data) { console.log(data); // 服务器返回:handleData({...}) } const script = document.createElement('script'); script.src = 'https://api.example.com/data?callback=handleData'; document.head.appendChild(script);