近日,不少前端开发者在尝试直接使用TypeScript编写的代码在浏览器中运行时,遇到了一个典型的错误提示:“Uncaught ReferenceError: exports is not defined”。这一错误虽非首次出现,却暴露出许多初学者甚至经验丰富的开发者对TypeScript编译输出与浏览器环境适配的误解。本文将详细解析该错误的成因,并提供几种行之有效的解决方案。
错误重现:一个常见的场景
假设你有一个简单的TypeScript文件 math.ts:
export function add(a: number, b: number): number {
return a + b;
}
你在HTML中通过 <script type="module" src="math.js"></script> 引入编译后的JavaScript文件。然而,打开浏览器控制台时,却看到红色的错误:Uncaught ReferenceError: exports is not defined。更令人困惑的是,编译后的 math.js 文件中确实出现了类似 Object.defineProperty(exports, "__esModule", { value: true }); exports.add = add; 的代码,而 exports 在浏览器环境中并不存在。
根源剖析:TypeScript的模块系统设定
要理解这个错误,需要先了解TypeScript的编译配置。TypeScript编译器(tsc)允许通过 tsconfig.json 中的 module 选项指定输出代码的模块系统。常见的取值包括 CommonJS、ES2015(即ES modules)、AMD、UMD 等。当 module 设置为 CommonJS(或者未显式指定时,默认值在某些项目中可能为 CommonJS)时,编译器会生成类似 exports.add = add 的代码,这些代码依赖于Node.js环境中的 module 和 exports 对象。
浏览器原生并不识别CommonJS模块规范——它没有 exports 全局变量,也没有 require 函数。因此,当浏览器尝试执行这些编译后的代码时,会立即抛出 exports is not defined 的错误。
常见误区:为何会出现错误的默认配置?
很多开发者在使用TypeScript时,往往直接采用 tsc --init 生成的默认 tsconfig.json。在早期的TypeScript版本中,默认的 module 值是 commonjs。此外,一些在线教程或项目模板可能沿用了这一配置,导致开发者在浏览器环境直接引用生成文件时触发错误。另外,一些基于Node.js构建的前端项目(例如使用Express后端同时提供前端资源)可能会误将服务端模块配置应用到客户端代码上。
解决方案:让TypeScript正确适应浏览器
针对这一问题,有以下几种常见的解决办法:
1. 修改tsconfig.json模块输出为ES模块
这是最直接的方法。将 compilerOptions 中的 module 设置为 "ES2015" 或 "ESNext",并确保 target 也兼容(例如 "ES6" 以上)。这样编译器输出会采用原生的 import/export 语法,浏览器可以直接通过 <script type="module"> 加载。
{
"compilerOptions": {
"module": "ES2015",
"target": "ES6",
"outDir": "./dist"
}
}
生成的文件将包含 export function add,不再依赖 exports 对象。
2. 使用打包工具(Bundler)
如果你希望代码兼容旧浏览器或需要处理更多依赖,使用Webpack、Vite、Parcel、esbuild等工具对TypeScript进行打包是更工业级的选择。这些打包器会分析模块依赖,将CommonJS、ES模块等统一转换为可在浏览器中安全运行的单一文件(或分块),并自动处理 exports、require 等变量。例如,在Webpack配置中使用 ts-loader 或 babel-loader 配合 @babel/preset-typescript,打包后便无此问题。
3. 手动声明全局变量(不推荐)
在某些临时调试场景下,你可以在脚本加载前手动定义 exports 和 module:
<script>var exports = {}; var module = { exports: exports };</script>
<script src="math.js"></script>
但这仅适用于极简单的示例,生产环境绝不建议采用,因为会破坏模块隔离性且容易引发其他副作用。
影响与启示
这一错误提醒我们,TypeScript仅是一个类型系统和语法工具,其编译输出依赖正确的配置。开发者在将TypeScript用于浏览器前端时,必须明确模块系统选择。许多现代框架(如React、Vue、Angular)的脚手架已经默认使用ES模块或打包器,绕过这一陷阱。但当你尝试手动搭建项目或编写独立的TypeScript库时,仍需留意 module 设置。
此外,随着ES模块在浏览器中的普及度越来越高(支持率现已超过95%),直接使用ES modules输出已成为最佳实践,既简洁又无需额外工具。对于需要兼容旧环境的大型项目,打包工具则是最稳妥的保障。
总之,当你在浏览器中遇到“exports is not defined”时,不必惊慌。检查你的 tsconfig.json 配置,选择正确的模块系统,或者引入一个可靠的打包流程,问题便会迎刃而解。开发无小事,细节定成败,正确的配置就是最好的预防。