不能直接在<head>里写script加载第三方SDK,因为会阻塞HTML解析拖慢首屏,且可能因window或document未就绪导致ReferenceError;应优先用defer确保DOM就绪后执行,或按SDK要求选择async,内联脚本须包裹在DOMContentLoaded监听器或自调用函数中。

为什么不能直接在 <head> 里写 script 加载第三方 SDK?
直接把第三方 SDK 的初始化脚本(比如 analytics.js、segment-analytics.min.js)写死在 <head> 里,最常引发两个问题:一是阻塞 HTML 解析,拖慢首屏;二是 SDK 可能依赖 window 或 document 尚未就绪的环境,导致 ReferenceError 或初始化失败。
用 async 还是 defer?选错就白配
async 适合纯独立脚本(如统计埋点),它不保证执行顺序,且可能在 DOM 构建中途执行;defer 更稳妥——它会等 HTML 解析完、DOM 构建完成后再按顺序执行,适合需要访问 document 或依赖其他脚本的 SDK 初始化逻辑。
- 如果 SDK 文档明确说“必须在 DOMContentLoaded 前运行”,用
defer - 如果只是发个 beacon 且无 DOM 依赖,
async可略快,但风险更高 - 千万别对内联脚本加
async或defer—— 它们只对src外链有效
内联脚本必须包裹在 DOMContentLoaded 或自调用函数里
哪怕脚本写在 <head> 里,只要它操作了 document.body 或调用了 document.querySelector,就必须确保 DOM 已就绪。最简方案是用 DOMContentLoaded 监听器,或更轻量的自调用函数判断 document.readyState。
<script>
function initThirdPartySDK() {
// 这里放 SDK 初始化代码,比如 window.analytics.load('KEY')
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initThirdPartySDK);
} else {
initThirdPartySDK();
}
</script>
- 不要用
window.onload—— 它等所有资源(图片、字体)加载完,太晚 - 避免直接写
document.getElementById(...)在顶层作用域 —— 此时元素大概率不存在 - 如果 SDK 提供了异步加载器(如 Segment 的
!function(){...}()模式),优先用它的官方内联写法
内联脚本里引用外链 SDK 时,注意 CSP 和 SRI
很多团队以为“内联脚本”=“安全”,其实只要它动态创建 <script src="https://cdn.example.com/sdk.js">,就仍受 CSP script-src 策略限制,且无法享受 SRI 校验。
立即学习“前端免费学习笔记(深入)”;
- CSP 必须显式允许目标域名:
script-src 'self' https://cdn.segment.com https://www.google-analytics.com; - 如果用
fetch+eval加载远程脚本,CSP 会直接拦截(且极度危险,禁止) - SRI 只对带
integrity属性的外链生效,内联脚本本身无法加 SRI —— 所以关键 SDK 还是该用带 integrity 的外链 +defer
真正“安全内联”的底线是:脚本内容完全可控、无动态拼接 URL、不 eval 任何远程代码、且 CSP 允许其执行上下文。否则,所谓内联只是把风险从网络转移到了 HTML 源码里。



















