JavaScript 中的 script 标签问题详解
1. script 标签的核心属性
type 属性
type="text/javascript"
:默认值,可省略type="module"
:将脚本作为 ES6 模块处理,支持 import/exporttype="application/json"
:包含 JSON 数据type="application/ld+json"
:结构化数据type="importmap"
:定义模块导入映射
加载控制属性
async
:异步加载,加载完立即执行,不阻塞解析defer
:延迟执行,等 DOM 解析完成后按顺序执行nomodule
:在支持 ES 模块的浏览器中忽略此脚本
安全相关属性
integrity
:子资源完整性检查crossorigin
:跨域资源共享设置referrerpolicy
:控制 HTTP 请求的 Referer 头
其他属性
src
:外部脚本的 URLcharset
:已废弃,使用 HTTP 头或 meta 标签language
:已废弃,使用 type 替代
2. 常见问题与解决方案
问题:Cannot use import statement outside a module
错误信息:
Uncaught SyntaxError: Cannot use import statement outside a module (at buriedLogic.js:1:1)
问题原因: 使用了 ES6 的 import/export 语法,但脚本没有被当作模块加载。
示例代码:
// buriedLogic.js
import Config from "./config.js";
import BuriedPoint from "./buriedPoint.js";
import AppJump from "./appJump.js";
class BuriedLogic {
// 类定义...
}
解决方案:
方法一:在 HTML 中添加 type="module"属性(推荐)
<script type="module" src="buriedLogic.js"></script>
方法二:不使用 ES6 模块语法,改为传统方式
// 移除import语句 // 使用自执行函数封装所有代码 (function () { // 将所有代码放在这个作用域内 // ... })();
为什么方法一有效:
添加
type="module"
后,浏览器以 ES 模块规范解析脚本模块有自己的作用域,自动采用严格模式
支持 import/export 语法
模块默认延迟加载(类似 defer)
每个模块只执行一次
3. 传统脚本与 ES 模块的区别
传统脚本 (<script>
)
共享全局作用域
按顺序执行
没有 import/export 机制
变量提升至全局
默认同步加载
ES 模块 (<script type="module">
)
每个模块有独立作用域
自动采用严格模式
支持 import/export
只执行一次
默认延迟加载
受跨域限制
4. 常见使用模式
传统同步加载
<script src="app.js"></script>
延迟加载
<script defer src="app.js"></script>
异步加载
<script async src="app.js"></script>
模块加载
<script type="module" src="app.js"></script>
回退方案
<script type="module" src="modern.js"></script>
<script nomodule src="legacy.js"></script>
内联 JSON 数据
<script type="application/json" id="data">
{ "name": "张三", "age": 30 }
</script>
5. 实际案例分析
在我们的重构项目中,遇到了模块化相关的错误:
项目结构:
- config.js // 配置文件
- utils.js // 工具函数
- httpService.js // HTTP请求封装
- buriedPoint.js // 埋点逻辑
- appJump.js // 跳转逻辑
- buriedLogic.js // 主文件
错误: 当我们将代码重构为模块化结构时,浏览器报错:
Uncaught SyntaxError: Cannot use import statement outside a module (at buriedLogic.js:1:1)
解决方法: 在 HTML 中将 script 标签改为:
<script type="module" src="buriedLogic.js"></script>
结果: 问题成功解决,模块化代码正常运行。
6. 最佳实践
使用
defer
替代阻塞脚本使用
type="module"
进行模块化开发添加
integrity
属性增强安全性避免过多内联脚本
使用构建工具管理复杂依赖
明确模块间依赖关系
按职责拆分模块
7. 现代前端构建工具
对于复杂项目,建议使用构建工具来处理模块依赖:
Webpack:功能全面的打包工具
Rollup:适合库开发的打包工具
Vite:基于 ES 模块的开发服务器和构建工具
Parcel:零配置的 Web 应用打包工具
这些工具可以自动处理模块间依赖,生成兼容性更好的代码,减少手动处理模块相关问题的需要。