使用<script src>易出错的核心在于浏览器解析机制:file://协议下外部脚本被安全策略禁止加载,须启HTTP服务;async与defer执行时机迥异,混用或误选致DOM访问失败;无属性脚本应置</body>前或用defer放<head>;src与内联代码不可共存;onerror无法捕获语法及运行时错误。

直接用 <script src="xxx.js"></script> 很容易出错——不是路径 404,就是 DOM 找不到,或者执行时机错乱。核心问题不在“会不会写”,而在“浏览器怎么解析这个标签”。
src 路径写错:file:// 协议下必然失败
开发时双击 HTML 文件打开,浏览器走 file:// 协议,此时 src="js/main.js" 或 src="/assets/app.js" 都会报 net::ERR_FILE_NOT_FOUND。这不是路径写得不够“对”,而是现代浏览器禁止从本地文件系统加载外部脚本(安全策略)。
- 必须启动本地 HTTP 服务:比如
python3 -m http.server 8000或 VS Code 的 Live Server 插件 - 路径要明确语义:
src="./js/main.js"(同级js/目录)、src="/static/index.js"(站点根目录,对应http://localhost:8000/static/index.js) - 避免无前缀写法如
src="main.js"——它会被解析为“当前 HTML 所在目录下的main.js”,一旦 HTML 移动位置就失效
async 和 defer 混用或误选:DOM 访问失败的根源
async 和 defer 都让脚本异步下载,但执行时机天差地别。写错一个属性,document.getElementById("menu") 就返回 null。
-
async:下载完立刻执行,不等 DOM,也不保证顺序——适合 Google Analytics 这类完全独立的统计脚本 -
defer:下载异步,但执行严格在 DOM 解析完成之后、DOMContentLoaded之前,且按<script>出现顺序执行——适合初始化菜单、绑定按钮事件等依赖 DOM 的逻辑 - 不能同时写
async defer:后者被忽略,浏览器只认async -
type="module"脚本默认行为等价于defer,且启用 ES 模块隔离,this是undefined,不自动挂全局变量
script 标签位置放错:白屏或执行中断
没加 async 或 defer 的外部脚本,默认同步阻塞:浏览器停在 <script src="a.js"></script> 处,等它下载、解析、执行完才继续构建 DOM。用户看到的是空白页或卡在某一块。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 传统稳妥做法:把无属性的
<script src="xxx.js"></script>放在</body>前——DOM 已就绪,脚本执行不会报null - 用
defer时可放心放<head>:加载不阻塞,执行有保障,语义也更清晰 - 绝对不要在
<head>里放无属性的外部脚本,尤其当它内部有document.body.appendChild()这类操作
src 和内联代码混写:内容被静默丢弃
<script src="a.js">console.log("test");</script> 这种写法,第二行代码永远不会执行。只要用了 src 属性,标签体内的所有内容都会被浏览器忽略。
- 调试时想临时加点逻辑?删掉
src,把代码写进标签体;上线前再拆成外链 - 内联脚本不支持
async或defer,写了也无效 - 构建工具(如 Vite)的
import写法属于 ES 模块语法,和传统<script>不是一回事,别拿它当“内联替代方案”去套用
最容易被忽略的一点:错误捕获靠 onerror 只能抓到加载失败(404、网络断),抓不到语法错误或运行时报错;而多个 defer 脚本即使某个执行出错,后续脚本仍会继续执行——没人帮你兜底。

















