最简方式是,但加载失败主因是路径错误、file://协议限制、async/defer误用、动态插入时机不当及csp或type属性隐式影响。

直接用 <script src="xxx.js"></script> 是最简方式,但绝大多数加载失败不是语法问题,而是路径、协议或执行时机失控——尤其开发阶段双击 HTML 打开时,file:// 协议会让所有 src 加载静默失败。
src 路径写错:404 不报错,但脚本根本没发请求
浏览器对 src 的路径解析只认当前 HTML 文件所在位置,不看 JS 文件在哪。写 src="js/main.js" 看似合理,一旦 HTML 被挪进子目录(比如从 /index.html 移到 /page/index.html),路径就失效了。
- 明确用
./js/main.js(同级 js 目录)或/js/main.js(站点根目录起始),避免省略./或多层../ - 绝对路径的
/指的是 HTTP 服务的根,不是你电脑的 C:\;所以必须起本地服务,比如python3 -m http.server或 VS Code Live Server - 别双击 HTML 文件打开——此时地址栏是
file:///xxx/index.html,任何src都会触发net::ERR_FILE_NOT_FOUND或被 CSP 拦截 - 查 Network 面板,过滤
JS,看请求状态码和 “Initiator” 列,能快速定位哪一行<script></script>触发了失败请求
async 和 defer 选错:DOM 元素拿不到,或执行顺序乱套
async 和 defer 都让下载不阻塞 HTML 解析,但执行时机天差地别。混用、误用或放错位置,document.getElementById() 就大概率返回 null。
- 依赖 DOM 的脚本(如初始化菜单、绑定按钮点击)必须用
defer:它保证在 DOM 解析完、DOMContentLoaded前,按<script></script>出现顺序执行 - 完全独立的脚本(如 Google Analytics、埋点 SDK)可用
async:下载完立刻执行,不等 DOM,也不保序 - 不要同时写
async defer——后者会被忽略,浏览器只认async -
defer只对外部脚本(带src)生效;内联脚本加了也白加
动态插入 script 标签:load 事件不是万能解药
用 document.createElement('script') + appendChild 加载外部 JS,看似可控,实则默认行为接近 async:不等 DOM 就绪,执行顺序也不保。
- 仅监听
script.onload不够——它只表示文件下载完成,不代表 DOM 已就绪,更不代表其他脚本已执行 - 若需操作
document.body,得显式判断:if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', fn) },或直接检查document.body是否存在 - 多个动态脚本要串行执行?别靠
onload嵌套,改用Promise链或import()(如果目标环境支持模块) - CSP 策略如
script-src 'self'会拦截动态创建的<script></script>,控制台报Refused to load the script,此时只能改用nonce或预声明
真正容易被忽略的点是:type 属性缺省时,现代浏览器可能把无 type 的外部脚本当 type="module" 处理——这会自动启用 defer 行为,但也意味着不能用 document.write,且变量不会自动挂到 window 上。如果项目里混用传统脚本和模块,全局变量引用很容易突然失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











