近日,不少前端开发者在尝试直接使用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 选项指定输出代码的模块系统。常见的取值包括 CommonJSES2015(即ES modules)、AMDUMD 等。当 module 设置为 CommonJS(或者未显式指定时,默认值在某些项目中可能为 CommonJS)时,编译器会生成类似 exports.add = add 的代码,这些代码依赖于Node.js环境中的 moduleexports 对象。

浏览器原生并不识别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模块等统一转换为可在浏览器中安全运行的单一文件(或分块),并自动处理 exportsrequire 等变量。例如,在Webpack配置中使用 ts-loaderbabel-loader 配合 @babel/preset-typescript,打包后便无此问题。

3. 手动声明全局变量(不推荐)

在某些临时调试场景下,你可以在脚本加载前手动定义 exportsmodule

<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 配置,选择正确的模块系统,或者引入一个可靠的打包流程,问题便会迎刃而解。开发无小事,细节定成败,正确的配置就是最好的预防。