script 标签的 src 属性必须是有效路径,相对路径以 html 文件为基准;使用 src 时标签内 js 不执行;路径错误会报 404 或 err_aborted;禁用 file:// 协议,需本地服务;defer 等 dom 构建完按序执行,async 下载完立即执行无序;推荐 defer 放 head;es module 需 type="module" 且路径带 .js。

script 标签的 src 属性必须是有效路径,相对路径以 HTML 文件为基准
浏览器加载 <script></script> 时,如果用了 src,就**不会执行标签内的 JS 代码**,只加载外部文件。很多人写成 <script src="js/main.js">console.log('test');</script>,结果发现 console.log 完全没执行——这是最常踩的坑。
- 路径错误时,浏览器控制台会报
Failed to load resource: net::ERR_ABORTED或404,检查 Network 面板里请求的完整 URL 是否拼错 - 用相对路径(如
./js/app.js、../lib/utils.js)时,基准是当前 HTML 文件所在目录,不是 JS 文件位置,也不是服务器根目录 - 避免用
file://协议直接双击打开 HTML:现代浏览器会因跨域限制拒绝加载本地src的 JS,必须起一个本地服务(比如npx serve或 VS Code Live Server 插件)
defer 和 async 的行为差异直接影响执行时机和 DOM 可用性
默认情况下,<script src="..."></script> 是同步阻塞的:HTML 解析暂停 → JS 下载 → JS 执行 → 继续解析。加 defer 或 async 能改变这个流程,但二者逻辑完全不同。
-
defer:JS 下载不阻塞 HTML 解析,但**等整个 HTML 解析完、DOM 构建完成后再按顺序执行**,适合操作 DOM 的脚本(如初始化按钮事件) -
async:JS 下载不阻塞,**下载完立刻执行,不保证顺序**,适合无依赖的独立逻辑(如统计埋点、广告脚本) - 两者都只对带
src的<script></script>生效;内联脚本(没src)加了也没用 - 多个
defer脚本按书写顺序执行;多个async脚本谁先下载完谁先执行,顺序不可控
放在 还是 底部?取决于你是否需要立即操作 DOM
老教程总说“放 底部”,其实现在更推荐用 defer 放 ——语义清晰、加载早、执行可控。只有明确知道 JS 不依赖 DOM,或想彻底避开 DOM 操作时机问题时,才考虑其他方式。
- 如果 JS 里写了
document.getElementById('app'),但脚本在且没加defer,大概率拿到null,因为此时 DOM 还没开始解析 - 放
底部(即所有 HTML 标签之后)能自然保证 DOM 就绪,但会延迟 JS 下载,尤其页面内容多时 - 现代项目中,更稳妥的是统一用
<script defer src="main.js"></script>放在,既早下载又晚执行
ES Module 场景下 import 和 script src 共存要注意类型声明
当你用 import 语法(比如 import { init } from './utils.js'),就不能再用普通 <script src="..."></script> 引入同一个文件,否则会报 Uncaught SyntaxError: Cannot use import statement outside a module。
立即学习“Java免费学习笔记(深入)”;
- 启用 ES Module 必须显式声明
<script type="module" src="main.js"></script>,否则浏览器当普通脚本处理 - 模块脚本默认是
defer行为,不用额外加defer - 模块内不能用
var/function声明全局变量,变量作用域仅限于模块;想暴露给全局要用window.xxx = xxx - 模块路径必须带扩展名(如
./utils.js),不能省略.js,否则报 404










