<script> 元素用作嵌入或者引用 JavaScript 代码,其自身支持 11 个属性:async、crossorigin、defer、fetchpriority、integrity、nomodule、nonce、referrerpolicy、src、type 和 blocking。其中有一些我们常用或者相对有意思的。
-
async浏览器在解析 DOM 的时候,遇到存在
async属性的<script>元素时,浏览器会继续解析 DOM(不阻塞 DOM 解析),并且并行地异步请求外联的 JS 资源,等到 JS 请求完成后会立马执行 JS 资源(此时会阻塞 DOM 的解析,并且无法保证 JS 资源的执行顺序,取决于资源大小和加载时的网络情况)。 -
deferdefer与async属性的区别在于执行时机,两者都是异步并行下载,下载过程不会阻塞 DOM 解析,defer属性标识的 JS 资源会在文档被解析后,触发DOMContentLoaded(何时会触发此事件?留个思考,下篇文章将会说明) 事件之前执行。 注意:存在多个<script>元素同时使用defer时,会按照它们出现在文档中的顺序执行。 -
nomodule<script>元素设置此属性时,在不支持ES 模块的浏览器会执行此 script 内的 JS 脚本。 -
src大家都会的,值为外联 JS 脚本的地址。
-
type如果是 JS 资源时,MDN 鼓励直接省略,因为 MIME 类型并没有跨浏览器标准化,无效或无法识别的 MIME 类型浏览器可能直接跳过相关代码。另外还有 2 个其他有意思的值:
-
module: 告诉浏览器我这段 script 代码是模块代码,要按照 ES6 模型那样写 import、export 等代码。模块代码默认是defer延后执行的。<script type="module"> import { hello } from './utils.js'; // 使用导入的函数 hello(); </script> -
importmap: 导入映射表是一个 JSON 对象,开发者可以用它来控制浏览器在导入 JS 模块时如何解析模块标识符。<script type="importmap"> { imports: { moment: "./utils/es/moment.js", antd: "./utils/es/antd.js" } } </script> <script type="module"> import test from 'test'; import test2 from 'test2'; </script>
-
此外,需要注意或者了解的其他点有以下几个:
- 在使用行内 JS 代码时,要注意代码中不能出现字符串
</script>,会误导浏览器解析导致报错:
<script>
// 错误写法
function error() {
console.log("</script>");
}
// 正确写法
function right() {
console.log("<\/script>"); // 在 / 前增加转义符 \
}
</script>
-
解释执行 JS 代码会阻塞页面解析,例如: DOM 解析,样式解析;
-
<script>元素中的src属性的值不一定要是 .js 后缀的文件,浏览器不会校验后缀,但是为了保证浏览器能正确解析 JS 文件,服务器响应的 JS 文件的Content-Type必须是 JS MIME 类型;
-
如果既在
<script>元素里面写了行内 JS 代码,也为其设置了src属性值,那么浏览器会忽略行内代码而是去下载src中的 JS 资源; -
在没有设置
defer和async属性的情况下,浏览器都会按照<script>在页面中出现的顺序下载和解析 JS 资源; -
将
<script>元素放在</body>标签前面可以防止阻塞页面解析,避免导致页面白屏时间过长。
<!DOCTYPE html>
<html>
<head>
<title>示例</title>
</head>
<body>
<!-- 这里是页面内容 -->
<script src="example.js"></script>
</body>
</html>