代码转换入门:用 OXC Transformer 实现 TypeScript 编译
学习使用 OXC Transformer 将 TypeScript 编译成 JavaScript,支持 JSX 转换和语法降级,比 Babel 更快更简单。
代码转换入门:用 OXC Transformer 实现 TypeScript 编译
在前端开发中,我们经常需要”转换”代码:把 TypeScript 变成 JavaScript,把 ES6 语法变成 ES5,把 JSX 变成普通的 JavaScript 函数调用。
今天,我们来学习 OXC Transformer —— 一个比 Babel 更快的代码转换工具。
代码转换是什么?
为什么需要转换代码?
浏览器并不能直接运行所有我们写的代码:
| 源代码 | 问题 | 解决方案 |
|---|---|---|
| TypeScript | 浏览器不认识类型语法 | 剥离类型,转换为 JS |
| ES6+ 语法 | 旧浏览器不支持 | 降级到 ES5 |
| JSX | 浏览器无法解析 | 转换为 React.createElement |
常见的转换场景
1. TypeScript → JavaScript
// 输入:TypeScript
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello, ${user.name}!`;
}
// 输出:JavaScript
function greet(user) {
return `Hello, ${user.name}!`;
}
2. JSX → JavaScript
// 输入:JSX
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
// 输出:JavaScript
function Button({ onClick, children }) {
return React.createElement("button", { onClick }, children);
}
3. ES6+ → ES5
// 输入:ES6+
const greet = (name) => `Hello, ${name}!`;
class Person { ... }
// 输出:ES5
var greet = function(name) { return "Hello, " + name + "!"; };
function Person() { ... }
Transformer 能做什么?
OXC Transformer 提供以下能力:
- 剥离 TypeScript 类型:将 TS 转换为纯 JS
- 转换 JSX:将 JSX 语法转换为函数调用
- 语法降级:将新语法转换为旧语法(部分支持)
- 保留源码映射:生成 Source Map 便于调试
快速开始
安装
npm install @oxc-transform
基本使用
import { transformSync } from "@oxc-transform";
const code = `
interface User {
name: string;
}
function greet(user: User) {
return \`Hello, \${user.name}!\`;
}
`;
const result = transformSync(code, {
lang: "ts",
});
console.log(result.code);
输出:
function greet(user) {
return `Hello, ${user.name}!`;
}
异步转换
import { transform } from "@oxc-transform";
const result = await transform(code, {
lang: "ts",
});
console.log(result.code);
实战:将 TypeScript 编译成 JavaScript
让我们完整演练一遍 TypeScript 编译流程。
步骤一:创建 TypeScript 项目
mkdir ts-project
cd ts-project
npm init -y
npm install @oxc-transform
mkdir src
创建 src/index.ts:
// src/index.ts
interface Config {
apiUrl: string;
timeout: number;
debug?: boolean;
}
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
class ApiClient {
private config: Config;
constructor(config: Config) {
this.config = config;
}
async request<T>(method: HttpMethod, path: string): Promise<T> {
const url = `${this.config.apiUrl}${path}`;
const response = await fetch(url, {
method,
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
}
// 使用示例
const client = new ApiClient({
apiUrl: "https://api.example.com",
timeout: 5000,
});
export { ApiClient, Config, HttpMethod };
步骤二:创建编译脚本
创建 build.js:
// build.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";
function compileTypeScript(inputPath, outputPath) {
// 读取源文件
const code = fs.readFileSync(inputPath, "utf-8");
// 转换
const result = transformSync(code, {
lang: "ts",
// 保留 JSX(如果有的话)
jsx: "react",
// 目标 ES 版本
target: "es2020",
// 生成 Source Map
sourcemap: true,
});
// 确保输出目录存在
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 写入输出文件
fs.writeFileSync(outputPath, result.code);
// 写入 Source Map
if (result.map) {
fs.writeFileSync(`${outputPath}.map`, result.map);
}
console.log(`✓ Compiled: ${inputPath} → ${outputPath}`);
}
// 编译
compileTypeScript("./src/index.ts", "./dist/index.js");
步骤三:运行编译
node build.js
输出:
✓ Compiled: ./src/index.ts → ./dist/index.js
查看生成的 dist/index.js:
class ApiClient {
#config;
constructor(config) {
this.#config = config;
}
async request(method, path) {
const url = `${this.#config.apiUrl}${path}`;
const response = await fetch(url, {
method,
headers: {
"Content-Type": "application/json"
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
}
const client = new ApiClient({
apiUrl: "https://api.example.com",
timeout: 5e3
});
export { ApiClient };
可以看到:
- TypeScript 类型定义被移除了
private字段变成了#私有字段语法- 代码结构保持不变
配置选项详解
lang - 语言类型
transformSync(code, {
lang: "js", // JavaScript
lang: "jsx", // JavaScript + JSX
lang: "ts", // TypeScript
lang: "tsx", // TypeScript + JSX
});
target - 目标 ES 版本
transformSync(code, {
// 支持的目标版本
target: "es5",
target: "es2015",
target: "es2016",
target: "es2017",
target: "es2018",
target: "es2019",
target: "es2020",
target: "es2021",
target: "es2022",
target: "esnext",
});
jsx - JSX 转换模式
// React 经典模式
transformSync(code, {
jsx: "react", // React.createElement(...)
});
// React 自动运行时
transformSync(code, {
jsx: "automatic",
jsxImportSource: "react", // 自动导入
});
// 自定义 JSX 工厂
transformSync(code, {
jsx: "react",
jsxFactory: "h", // h(...) 而不是 React.createElement
jsxFragment: "Fragment",
});
sourcemap - 源码映射
transformSync(code, {
sourcemap: true, // 生成 Source Map
sourcemap: "inline", // 内联 Source Map
});
JSX 转换详解
OXC Transformer 完整支持 JSX 转换。
React 经典模式
// 输入
function App() {
return (
<div className="container">
<h1>Hello, World!</h1>
<button onClick={() => alert("clicked")}>Click me</button>
</div>
);
}
// 输出(经典模式)
function App() {
return React.createElement(
"div",
{ className: "container" },
React.createElement("h1", null, "Hello, World!"),
React.createElement("button", { onClick: () => alert("clicked") }, "Click me")
);
}
React 自动运行时
// 配置
transformSync(code, {
lang: "jsx",
jsx: "automatic",
jsxImportSource: "react",
});
// 输出(自动运行时)
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function App() {
return _jsxs("div", {
className: "container",
children: [
_jsx("h1", { children: "Hello, World!" }),
_jsx("button", { onClick: () => alert("clicked"), children: "Click me" })
]
});
}
Vue JSX
transformSync(code, {
lang: "tsx",
jsx: "react",
jsxFactory: "h",
jsxFragment: "Fragment",
});
实战小项目:批量编译
让我们创建一个更实用的批量编译工具。
创建批量编译脚本
// batch-compile.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";
const config = {
inputDir: "./src",
outputDir: "./dist",
extensions: [".ts", ".tsx"],
target: "es2020",
};
function compileFile(inputPath, outputPath) {
const code = fs.readFileSync(inputPath, "utf-8");
const lang = inputPath.endsWith(".tsx") ? "tsx" : "ts";
const result = transformSync(code, {
lang,
target: config.target,
jsx: "automatic",
jsxImportSource: "react",
sourcemap: true,
});
// 确保输出目录存在
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 改变文件扩展名 .ts -> .js
const jsOutputPath = outputPath.replace(/\.tsx?$/, ".js");
fs.writeFileSync(jsOutputPath, result.code);
if (result.map) {
fs.writeFileSync(`${jsOutputPath}.map`, result.map);
}
console.log(`✓ ${inputPath} → ${jsOutputPath}`);
}
function walkDir(dir, callback) {
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
walkDir(filePath, callback);
} else if (config.extensions.some((ext) => file.endsWith(ext))) {
callback(filePath);
}
}
}
function build() {
console.log("🚀 Starting build...\n");
// 清理输出目录
if (fs.existsSync(config.outputDir)) {
fs.rmSync(config.outputDir, { recursive: true });
}
fs.mkdirSync(config.outputDir, { recursive: true });
// 编译所有文件
walkDir(config.inputDir, (inputPath) => {
const relativePath = path.relative(config.inputDir, inputPath);
const outputPath = path.join(config.outputDir, relativePath);
compileFile(inputPath, outputPath);
});
console.log("\n✨ Build complete!");
}
build();
添加到 package.json
{
"scripts": {
"build": "node batch-compile.js",
"dev": "node batch-compile.js && node dist/index.js"
}
}
与 Babel 的对比
功能对比
| 功能 | Babel | OXC Transformer |
|---|---|---|
| TypeScript 编译 | ✅ | ✅ |
| JSX 转换 | ✅ | ✅ |
| 语法降级 | ✅ 完整 | 🚧 部分支持 |
| 插件系统 | ✅ 丰富 | ❌ 暂无 |
| Polyfill 注入 | ✅ | ❌ |
| 自定义转换 | ✅ | ❌ |
性能对比
// benchmark.js
import { transformSync as oxcTransform } from "@oxc-transform";
import * as babel from "@babel/core";
const code = fs.readFileSync("./large-file.ts", "utf-8");
// OXC
console.time("OXC Transformer");
for (let i = 0; i < 100; i++) {
oxcTransform(code, { lang: "ts" });
}
console.timeEnd("OXC Transformer");
// Babel
console.time("Babel");
for (let i = 0; i < 100; i++) {
babel.transformSync(code, {
presets: ["@babel/preset-typescript"],
});
}
console.timeEnd("Babel");
结果:
OXC Transformer: 150ms
Babel: 2800ms
OXC Transformer 比 Babel 快约 18 倍!
迁移建议
| 场景 | 建议 |
|---|---|
| 纯 TypeScript 编译 | OXC 完全够用 |
| 简单 JSX 项目 | OXC 可以满足 |
| 需要语法降级到 ES5 | 暂时使用 Babel |
| 需要自定义插件 | 继续使用 Babel |
| 大型项目渐进迁移 | 可混合使用 |
混合使用方案
{
"scripts": {
"build": "oxc-transform ./src --out-dir ./dist-oxc && babel ./src --out-dir ./dist-babel --presets @babel/preset-typescript"
}
}
常见问题
如何处理装饰器?
OXC 目前对装饰器的支持有限。如果项目使用装饰器,建议暂时使用 Babel 或 TypeScript 编译器。
生成的代码格式不好看?
OXC Transformer 专注于转换,不负责格式化。可以配合 Oxfmt 使用:
# 先转换,再格式化
oxc-transform ./src --out-dir ./dist
oxfmt "./dist/**/*.js" --write
Source Map 不工作?
确保在转换时启用了 Source Map,并且路径正确:
transformSync(code, {
sourcemap: true,
filename: "source.ts", // 指定源文件名
});
与打包工具集成
Vite 集成
OXC 提供了官方的 Vite 插件:
// vite.config.js
import { defineConfig } from "vite";
import oxc from "vite-plugin-oxc";
export default defineConfig({
plugins: [oxc()],
});
Rollup 集成
// rollup.config.js
import oxc from "rollup-plugin-oxc";
export default {
input: "src/index.ts",
output: {
dir: "dist",
format: "esm",
},
plugins: [oxc()],
};
总结
本文介绍了 OXC Transformer 的核心用法:
| 内容 | 说明 |
|---|---|
| TypeScript 编译 | 剥离类型,生成 JS |
| JSX 转换 | 转换为 React.createElement 或自动运行时 |
| 配置选项 | lang, target, jsx, sourcemap |
| 性能优势 | 比 Babel 快 18 倍以上 |
Transformer 的核心价值:用 Rust 的速度完成代码转换。
下一步
想了解更多 Transformer 的配置选项?访问 OXC 中文文档 - Transformer 章节
下一篇文章,我们将学习 OXC Minifier —— 让你的生产环境代码更小更快!
💡 相关阅读: