script 标签上有哪些属性?分别干啥用?
<script>这个标签看起来特别朴素:不就是引个 JS 吗?但只要你稍微做过一点性能优化、或者线上被 CDN 劫持/脚本顺序坑过一次,你就会发现:script 的属性不是装饰品,是“加载与执行规则说明书”。
下面按“最常用 → 容易踩坑 → 安全相关”的顺序,把属性都捋一遍。
先来一个最常见的 script
1 | <script src="https://cdn.example.com/app.js"></script> |
默认行为是:
- 浏览器解析到它会开始下载脚本
- 下载完会立刻执行
- 执行期间会阻塞 HTML 解析(你可以理解成:主线程被叫去“先把这段 JS 跑完”)
所以很多属性,本质都是在解决两个问题:
- 别卡页面(性能)
- 别出事(安全/一致性)
一、决定“下哪里”:src
src
指定外部脚本地址。
1 | <script src="/assets/app.js"></script> |
注意点:
- 没有
src就是内联脚本:<script>...</script> - 同一个
src重复写两次,浏览器未必会傻乎乎执行两次,但也别指望它帮你兜底,工程里最好别这么干
二、决定“怎么解析/怎么跑”:type
type
历史上 type="text/javascript" 很常见,现在多数情况下不写也行(默认就是 JS)。
但 type 有一个很重要的现代用法:
type=”module”
1 | <script type="module" src="/main.js"></script> |
它会把脚本当作 ES Module 处理,大体有这些特性:
- 支持
import/export - 默认是延后执行的(行为更像
defer) - 模块脚本默认是严格模式
- 同一个模块不会被重复执行(会按模块图做缓存/复用)
你可以把它理解成:“浏览器原生支持的打包/依赖系统”(当然没 bundler 那么全能,但语义是这个方向)。
三、决定“会不会阻塞页面”:async / defer
这俩是最容易被问,也最容易被用错的。
先给一个不那么严谨但很好记的结论:
defer:保证顺序,等 HTML 解析完再执行async:谁先下完谁先执行,不保证顺序
defer
1 | <script src="/a.js" defer></script> |
- 不阻塞 HTML 解析
- 按标签出现的顺序执行(a 先于 b)
- 一般会在 DOM 解析完成后、
DOMContentLoaded之前执行
适合:
- 大多数业务脚本(尤其有依赖顺序的那种)
- 你想“别卡首屏,但又别乱序”
async
1 | <script src="/a.js" async></script> |
- 不阻塞 HTML 解析
- 谁先下载完谁先执行(顺序不确定)
- 执行时仍然会抢主线程(所以如果脚本本身很重,用户照样可能卡一下)
适合:
- 独立脚本:埋点、监控、广告这类“你不依赖我、我也不依赖你”的
一个常见问题:async 和 defer 能一起写吗?
你可以写,但多数浏览器会按各自规则处理,实际心智会更乱。工程里建议直接选一个:要顺序就 defer,要不管顺序就 async。
四、模块脚本的好搭档:nomodule
nomodule
用来做兼容:支持 module 的浏览器跑 type="module",不支持 module 的浏览器跑 nomodule。
1 | <script type="module" src="/main.esm.js"></script> |
这套写法的好处是非常直白:现代浏览器吃现代包,老浏览器吃兼容包。
五、安全相关:integrity / crossorigin / nonce / referrerpolicy
这几位属于“平时不显山露水,出事时能救命”的属性。
integrity(SRI,子资源完整性校验)
让浏览器校验脚本内容有没有被篡改(比如 CDN 被污染、传输被劫持)。
1 | <script |
校验不过,浏览器会拒绝执行。
crossorigin
控制跨域请求的凭证与 CORS 行为,常见取值:
anonymous:不带 cookie 等凭证use-credentials:会带 cookie(前提是服务端允许)
它经常和 integrity 一起出现。直觉理解就行:你既然要做完整性校验,就得让浏览器用“可校验的方式”去拿资源。
nonce(配合 CSP)
当你开启了 CSP(Content Security Policy),内联脚本通常会被禁掉。nonce 用来做“白名单通行证”。
1 | <script nonce="rAnd0m"> |
nonce 一般由服务端动态生成并写入响应头/HTML。核心思路是:只有带了这个 nonce 的脚本才允许执行。
referrerpolicy
控制请求脚本时 Referer 头怎么带,典型用法是减少隐私泄露或避免把路径参数带给第三方。
1 | <script src="https://third-party.example.com/a.js" referrerpolicy="no-referrer"></script> |
常见取值你不用全背,记住两三个就够用:
no-referrer:完全不带origin:只带源(协议 + 域名 + 端口)strict-origin-when-cross-origin:现在很多浏览器的默认行为
六、一些“你可能见过,但别太依赖”的属性
charset(基本算历史遗留)
1 | <script src="/a.js" charset="utf-8"></script> |
以前用于指定外部脚本编码。现代项目基本统一 UTF-8,这个属性存在感很低。
language(过时)
很早以前用来写 language="javascript",现在不建议用。
id / class / data-*
这些属于通用属性:
id/class:给你选中 DOM 用data-*:给脚本传一点配置
1 | <script |
这种写法很适合第三方 SDK:你不用在全局挂一堆变量,它自己读 dataset 就能初始化。
七、我平时怎么选?(快速建议)
- 业务主包:优先
defer(既不阻塞解析,又能保证顺序) - 第三方独立脚本(监控/埋点):用
async(别挡正事) - 想走现代语法与更好的缓存模型:
type="module"+nomodule做双产物 - 上 CDN 的关键依赖:考虑
integrity+crossorigin="anonymous"(别问,问就是吃过亏) - 公司安全策略比较严(CSP):准备好
nonce
结尾
script 标签的属性其实就两句话:
- 你想让浏览器“什么时候下载、什么时候执行”
- 你想让脚本“更安全、更可控”
把这几类属性搞清楚,很多“脚本顺序不对”“页面怎么突然卡一下”“线上怎么样式/初始化没了”的问题,都会少掉一半。
Happy Coding!
