script标签最稳妥的位置是放在</body>之前;放在head中需加defer或async,内联脚本不宜用于生产环境,外部脚本更利于缓存与维护,type属性影响执行行为,动态插入需注意加载时机与跨域配置。

script 标签必须写在什么位置才有效
浏览器解析 HTML 是从上到下流式执行的,<script> 标签的位置直接影响脚本能否访问 DOM 元素、是否阻塞渲染。最稳妥的做法是:把 <script> 放在 </body> 之前。
- 放在
<head>里但没加defer或async:脚本会立即下载并执行,此时document.body还没生成,document.getElementById()等操作大概率返回null - 加了
defer的<script>可以放<head>:它会等 HTML 解析完成再按顺序执行,适合依赖 DOM 的初始化逻辑 -
async脚本不保证执行顺序,且可能在 DOM 构建中途就运行,只适合完全独立的脚本(比如埋点、广告)
内联脚本和外部脚本怎么选
内联脚本(<script>console.log('hi')</script>)写起来快,但无法缓存、不好复用、压缩和调试都更麻烦;外部脚本(<script src="main.js"></script>)能被浏览器缓存,支持 sourcemap,也方便构建工具介入。
- 开发阶段小量调试可用内联,但上线前务必抽离到外部文件
- 如果脚本里用了
document.write(),只能用内联——但请直接放弃这个 API,它会清空整个页面 - 外部脚本路径别写相对根路径(如
/js/app.js),容易因路由或子目录部署出错;优先用相对路径(./js/app.js)或数据驱动的路径拼接
type 属性不是可有可无的
老写法 <script type="text/javascript"> 已过时,现代浏览器默认把 <script> 当 JS 执行。但如果你写的是模块、JSON、模板字符串,type 就成了关键开关。
-
type="module":启用 ES Module 语法,自动开启strict模式,且默认defer行为;注意它不支持document.write(),也不能用script标签的src加载非同源脚本(CORS 限制) -
type="application/json"或其他非 JS 类型:浏览器不会执行,只是把内容当数据节点挂载,适合预置配置(JSON.parse(script.textContent)) - 写了错误的
type(比如text/ecmascript)会导致脚本被忽略,连控制台都不会报错
动态插入 script 标签要注意加载时机
用 document.createElement('script') 插入脚本,看似灵活,但默认是异步行为:脚本下载完立刻执行,不等 DOM 就绪,也不保证顺序。
立即学习“前端免费学习笔记(深入)”;
- 想等 DOM 就绪再执行?监听
document.readyState === 'interactive'或用DOMContentLoaded事件包裹插入逻辑 - 要确保多个动态脚本按序执行?给每个
<script>加async="false"(仅对动态创建有效),或手动链式加载(script.onload = nextScript) - 动态脚本的
src如果是跨域地址,记得加crossorigin="anonymous",否则报错时拿不到详细堆栈信息
最容易被忽略的是:动态插入的模块脚本(type="module")不能通过 innerHTML 注入,必须用 createElement + appendChild,否则模块逻辑根本不会触发。











