5月28日 01:18

Babel 配置 TypeScript 和 React 的完整方案是什么?

Babel 如何编译 TypeScript 和 JSX?

Babel 的编译流程是:解析(Parse)→ 转换(Transform)→ 生成(Generate)。@babel/preset-typescript 的工作方式是直接剥离类型注解,而非像 tsc 那样做类型检查后编译。@babel/preset-react 负责将 JSX 转换为 React.createElement 调用(React 17+ 使用新的 jsx 运行时自动导入)。

这意味着 Babel 只负责语法转译,不做类型校验。项目中需要额外运行 tsc --noEmit 来完成类型检查。

基础配置方案

安装依赖

bash
# 核心依赖 npm install --save-dev @babel/core @babel/cli @babel/preset-env # TypeScript 支持 npm install --save-dev @babel/preset-typescript # React JSX 支持 npm install --save-dev @babel/preset-react # 运行时优化(减少重复 helper 代码) npm install --save @babel/runtime npm install --save-dev @babel/plugin-transform-runtime

babel.config.js 完整配置

javascript
module.exports = { presets: [ ['@babel/preset-env', { targets: { browsers: ['> 1%', 'last 2 versions', 'not ie <= 8'] }, useBuiltIns: 'usage', corejs: 3 }], '@babel/preset-typescript', ['@babel/preset-react', { runtime: 'automatic', // React 17+ 自动导入 jsx 运行时 development: process.env.NODE_ENV === 'development' }] ], plugins: [ ['@babel/plugin-transform-runtime', { corejs: 3, helpers: true, regenerator: true }] ] };

preset 执行顺序

Babel preset 的执行顺序是从后往前:先执行 preset-react(转译 JSX),再执行 preset-typescript(剥离类型),最后执行 preset-env(降级语法)。这个顺序保证了每一步拿到的代码都是合法的上一阶段输出。

Webpack 集成配置

javascript
// webpack.config.js module.exports = { module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-typescript', '@babel/preset-react' ] } } } ] }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'] } };

tsconfig.json 关键配置

json
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "jsx": "preserve", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "moduleResolution": "node", "isolatedModules": true, "noEmit": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }

核心要点:

  • "jsx": "preserve" 让 TS 保留 JSX 原样,由 Babel 负责转换
  • "noEmit": true 让 TS 只做类型检查,不输出文件,编译交给 Babel
  • "isolatedModules": true 确保每个文件可独立转译,符合 Babel 单文件处理的工作模式

Babel vs tsc:为什么项目选择 Babel?

对比项Babeltsc
类型检查不做完整检查
编译速度快(单文件转译)较慢(全量类型分析)
语法降级支持(preset-env + corejs)仅 target 降级
Polyfill按需注入(useBuiltIns)不提供
JSX 转换preset-reactjsx 选项
插件生态丰富(装饰器、路径别名等)有限

实际项目中通常两者配合使用:Babel 负责编译和 polyfill,tsc --noEmit 负责类型检查。

json
// package.json { "scripts": { "type-check": "tsc --noEmit", "build": "npm run type-check && babel src --out-dir dist --extensions '.ts,.tsx'" } }

高级配置

环境区分

javascript
// babel.config.js module.exports = { presets: ['@babel/preset-env', '@babel/preset-typescript', '@babel/preset-react'], env: { development: { plugins: ['react-refresh/babel'] }, production: { plugins: ['transform-remove-console'] }, test: { presets: [ ['@babel/preset-env', { targets: { node: 'current' } }] ] } } };

路径别名

javascript
// babel.config.js 添加 plugins: [ ['module-resolver', { root: ['./src'], alias: { '@': './src', '@components': './src/components', '@utils': './src/utils' } }] ]
json
// tsconfig.json 同步配置 { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@components/*": ["src/components/*"], "@utils/*": ["src/utils/*"] } } }

路径别名必须同时在 Babel 和 TypeScript 两处配置,否则编译通过但类型检查报错。

装饰器支持

bash
npm install --save-dev @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
javascript
plugins: [ ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }] ]

CSS Module 类型声明

typescript
// src/types/global.d.ts declare module '*.module.scss' { const classes: { readonly [key: string]: string }; export default classes; } declare module '*.css' { const content: { [className: string]: string }; export default content; }

热更新配置

javascript
// webpack.config.js const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); module.exports = (env) => { const isDev = env.NODE_ENV === 'development'; return { module: { rules: [{ test: /\.(ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { plugins: [isDev && 'react-refresh/babel'].filter(Boolean) } } }] }, plugins: [isDev && new ReactRefreshWebpackPlugin()].filter(Boolean) }; };

常见踩坑点

类型检查和编译分离后忘记运行 tsc:Babel 编译通过不代表类型正确,必须在 CI 中加入 tsc --noEmit 步骤。

preset-react 的 runtime 选项classic 模式下每个 JSX 文件需要手动 import Reactautomatic 模式自动注入,React 17+ 项目务必使用 automatic

corejs 版本不匹配@babel/preset-envcorejs 选项必须与实际安装的 core-js 大版本一致(2 或 3),否则 polyfill 注入失败。

路径别名不同步:修改 tsconfig.jsonpaths 后必须同步修改 Babel 的 module-resolver 配置,否则运行时找不到模块。

标签:Babel