2024年10月3日星期四

HTML 中 JavaScript 文件引用的类型( type="module"和type="text/javascript")区别

 在 HTML 中,引用 JavaScript 文件时,type="module"type="text/javascript" 有一些显著的区别。两者的主要区别在于它们如何处理 JavaScript 代码的执行上下文、作用域、依赖关系管理以及加载方式。以下是详细解释:

1. type="module"

type="module" 告诉浏览器该脚本是一个 ECMAScript 模块(ES Module),这种模块化方式引入了一些新的功能和特性:

特点

  1. 模块作用域

    • 使用 type="module" 的脚本文件具有独立的作用域,文件内的变量、函数不会自动暴露到全局作用域(即 window 对象)。
    • 每个模块文件都是独立的作用域,只在模块内部有效,不会影响其他模块或全局作用域。
  2. 默认启用严格模式

    • 模块代码会自动以严格模式 (use strict) 执行,因此无需显式地在代码中添加 "use strict"; 语句。
  3. 支持模块化导入导出

    • 支持 importexport 语法,可以从其他模块中导入变量、函数、对象等,也可以将当前模块中的内容导出供其他模块使用。
    • 例如:
      javascript
      // module1.js export const hello = 'Hello, World!'; // module2.js import { hello } from './module1.js'; console.log(hello); // 输出 'Hello, World!'
  4. 异步加载

    • type="module" 的脚本会被异步加载,类似于在 <script> 中添加 async 属性,这意味着它不会阻塞 HTML 的解析和渲染。
    • 可以利用 import 来动态加载其他模块,例如:
      javascript
      import('./someModule.js').then(module => { module.doSomething(); });
  5. CORS 限制

    • 通过 type="module" 引入的模块遵循 CORS (Cross-Origin Resource Sharing) 安全策略。这意味着如果你从不同源(如跨域的服务器)加载模块,必须满足 CORS 相关的头部信息。
  6. 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 是默认类型。

特点

  1. 全局作用域

    • type="text/javascript" 的脚本中定义的变量和函数会自动暴露到全局作用域(window 对象)。
    • 这意味着同一 HTML 文件或多个脚本文件中的全局变量可能会相互覆盖,导致命名冲突。
  2. 非模块化

    • 不支持 importexport 语法。
    • 无法使用 type="module" 提供的模块化特性,因此更适合传统的 JavaScript 代码(如全局变量和函数)。
  3. 按顺序同步执行

    • 默认情况下,type="text/javascript" 的脚本是按顺序同步执行的,这意味着浏览器会在加载和执行该脚本时暂停对后续 HTML 的解析。
    • 如果需要异步加载脚本,可以手动添加 asyncdefer 属性。
  4. 加载方式更宽松

    • 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"
作用域模块作用域(不会自动暴露到全局)全局作用域
导入导出支持支持 importexport 语法不支持
默认严格模式自动启用严格模式非严格模式(需要手动启用)
加载方式异步加载,类似于 defer同步加载,除非添加 asyncdefer
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"

没有评论: