在 HTML 中,引用 JavaScript 文件时,type="module"
和 type="text/javascript"
有一些显著的区别。两者的主要区别在于它们如何处理 JavaScript 代码的执行上下文、作用域、依赖关系管理以及加载方式。以下是详细解释:
1. type="module"
type="module"
告诉浏览器该脚本是一个 ECMAScript 模块(ES Module),这种模块化方式引入了一些新的功能和特性:
特点
模块作用域:
- 使用
type="module"
的脚本文件具有独立的作用域,文件内的变量、函数不会自动暴露到全局作用域(即window
对象)。 - 每个模块文件都是独立的作用域,只在模块内部有效,不会影响其他模块或全局作用域。
- 使用
默认启用严格模式:
- 模块代码会自动以严格模式 (
use strict
) 执行,因此无需显式地在代码中添加"use strict";
语句。
- 模块代码会自动以严格模式 (
支持模块化导入导出:
- 支持
import
和export
语法,可以从其他模块中导入变量、函数、对象等,也可以将当前模块中的内容导出供其他模块使用。 - 例如:javascript
// module1.js export const hello = 'Hello, World!'; // module2.js import { hello } from './module1.js'; console.log(hello); // 输出 'Hello, World!'
- 支持
异步加载:
type="module"
的脚本会被异步加载,类似于在<script>
中添加async
属性,这意味着它不会阻塞 HTML 的解析和渲染。- 可以利用
import
来动态加载其他模块,例如:javascriptimport('./someModule.js').then(module => { module.doSomething(); });
CORS 限制:
- 通过
type="module"
引入的模块遵循 CORS (Cross-Origin Resource Sharing) 安全策略。这意味着如果你从不同源(如跨域的服务器)加载模块,必须满足 CORS 相关的头部信息。
- 通过
defer
默认行为:- 模块化脚本会自动应用
defer
属性(即脚本会在文档完全解析之后再执行),即使你没有显式设置defer
,它也会被自动应用。
- 模块化脚本会自动应用
示例
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES Module Example</title>
</head>
<body>
<script type="module">
// 使用 ES Module 语法
import { hello } from './module.js';
console.log(hello);
</script>
</body>
</html>
注意事项
- 使用
type="module"
时,HTML 文件通常需要通过服务器来提供(例如通过http://localhost
),而不是直接用file://
协议打开本地文件,因为模块化脚本通常依赖 CORS 策略。 - 如果在本地测试,可以使用简单的 HTTP 服务器(如
python -m http.server
)来运行。
2. type="text/javascript"
type="text/javascript"
是标准的 JavaScript 脚本类型定义方式,用于普通的 JavaScript 文件加载和执行。这也是浏览器默认的脚本类型,在 HTML5 规范中已经可以省略 type
属性,因为 text/javascript
是默认类型。
特点
全局作用域:
- 在
type="text/javascript"
的脚本中定义的变量和函数会自动暴露到全局作用域(window
对象)。 - 这意味着同一 HTML 文件或多个脚本文件中的全局变量可能会相互覆盖,导致命名冲突。
- 在
非模块化:
- 不支持
import
和export
语法。 - 无法使用
type="module"
提供的模块化特性,因此更适合传统的 JavaScript 代码(如全局变量和函数)。
- 不支持
按顺序同步执行:
- 默认情况下,
type="text/javascript"
的脚本是按顺序同步执行的,这意味着浏览器会在加载和执行该脚本时暂停对后续 HTML 的解析。 - 如果需要异步加载脚本,可以手动添加
async
或defer
属性。
- 默认情况下,
加载方式更宽松:
type="text/javascript"
的脚本没有严格的 CORS 限制,可以更自由地加载不同源的脚本。
示例
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Text JavaScript Example</title>
</head>
<body>
<script type="text/javascript">
// 普通 JavaScript 代码
var message = 'Hello, World!';
console.log(message);
</script>
</body>
</html>
注意事项
type="text/javascript"
在 HTML5 中是可选的,因为这是默认类型。你可以省略type
属性,只写<script>
标签,效果是一样的。
3. 区别总结
特性 | type="module" | type="text/javascript" |
---|---|---|
作用域 | 模块作用域(不会自动暴露到全局) | 全局作用域 |
导入导出支持 | 支持 import 和 export 语法 | 不支持 |
默认严格模式 | 自动启用严格模式 | 非严格模式(需要手动启用) |
加载方式 | 异步加载,类似于 defer | 同步加载,除非添加 async 或 defer |
CORS 限制 | 遵循 CORS 策略(跨域时需正确的 CORS 头部) | 加载方式更宽松 |
默认行为 | 自动应用 defer | 不会自动应用 defer |
模块化特性(动态导入) | 支持 import() 进行动态导入 | 不支持动态导入 |
4. 何时使用 type="module"
和 type="text/javascript"
- 使用
type="module"
:当你希望利用 ES6 的模块化特性(如import/export
),或者希望将代码组织成独立的模块而不是污染全局作用域时。 - 使用
type="text/javascript"
:用于加载传统的 JavaScript 文件,或在使用第三方库时(如果该库不支持 ES 模块化)。
总结来说,如果你的项目需要模块化的结构(如前端框架和现代化 JavaScript 开发),推荐使用 type="module"
。
对于一些简单的脚本或历史遗留项目,可以继续使用 type="text/javascript"
。
没有评论:
发表评论