Vite Plugin
@zod-codepen/vite-plugin 提供了与 Vite 的无缝集成,允许你在构建时自动将 Zod 模式转换为独立的 TypeScript 代码文件,从而实现更好的代码分离和优化。
为什么需要 Vite Plugin?
在大型项目中,Zod 模式可能会变得非常复杂,包含大量的验证逻辑和类型定义。这会带来几个问题:
- 打包体积 - Zod 库本身需要被打包到最终代码中
- 运行时开销 - 模式的构建和验证都在运行时进行
- 代码分离 - 难以将模式定义与业务逻辑分离
- 共享困难 - 在不同项目间共享模式定义变得复杂
Vite plugin 通过在构建时将 Zod 模式序列化为纯 TypeScript 代码来解决这些问题。
安装
bash
npm install -D @zod-codepen/vite-pluginbash
pnpm add -D @zod-codepen/vite-pluginbash
yarn add -D @zod-codepen/vite-plugin基本配置
1. 配置 Vite
在 vite.config.ts 中添加插件:
typescript
import { defineConfig } from 'vite';
import zodCodepen from '@zod-codepen/vite-plugin';
export default defineConfig({
plugins: [
zodCodepen({
// 插件选项
})
]
});2. 标记需要转换的模式
使用特殊的导入后缀来标记需要转换的文件:
typescript
// 原始模式文件: schemas/user.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().positive().optional(),
});
export const CreateUserSchema = UserSchema.omit({ id: true });typescript
// 使用转换后的模式
import { UserSchema, CreateUserSchema } from './schemas/user?codepen';
// UserSchema 现在是序列化后的代码字符串
console.log(UserSchema);
// 输出: "z.object({ id: z.string().uuid(), ... })"配置选项
typescript
interface ZodCodepenViteOptions {
/**
* 包含模式转换的文件模式
* @default ['**/*.schema.ts', '**/*.schema.js']
*/
include?: string | string[];
/**
* 排除的文件模式
* @default ['node_modules/**']
*/
exclude?: string | string[];
/**
* 输出格式
* @default 'esm'
*/
format?: 'esm' | 'cjs' | 'iife';
/**
* 是否生成 source maps
* @default true
*/
sourcemap?: boolean;
/**
* 是否在开发模式下启用
* @default false
*/
enableInDev?: boolean;
/**
* 自定义转换函数
*/
transform?: (code: string, id: string) => string | null;
}使用场景
1. Schema 文档生成
自动生成 API 文档中的模式定义:
typescript
// vite.config.ts
import zodCodepen from '@zod-codepen/vite-plugin';
export default {
plugins: [
zodCodepen({
include: 'src/schemas/*.ts',
})
]
};typescript
// src/docs/api.md.ts
import schemas from '../schemas/*.ts?codepen';
export function generateAPIDocs() {
return Object.entries(schemas).map(([name, code]) => `
## ${name}
\`\`\`typescript
${code}
\`\`\`
`).join('\n');
}2. 模式验证分离
将验证逻辑与模式定义分离:
typescript
// schemas/user.schema.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
profile: z.object({
name: z.string(),
bio: z.string().optional(),
avatar: z.string().url().optional(),
}),
});typescript
// validators/user.ts
import { UserSchema } from '../schemas/user.schema?codepen';
import { z } from 'zod';
// 动态重建模式用于验证
const schema = eval(UserSchema);
export function validateUser(data: unknown) {
return schema.parse(data);
}3. 跨项目共享模式
生成可以在多个项目中使用的模式定义:
typescript
// build-schemas.ts
import { generateModule } from '@zod-codepen/zod-v3';
import * as schemas from './schemas';
import fs from 'fs';
// 构建时生成独立的模式文件
const code = generateModule(schemas);
fs.writeFileSync('dist/schemas.ts', code);4. 模式版本管理
跟踪模式的变更历史:
typescript
// vite.config.ts
import zodCodepen from '@zod-codepen/vite-plugin';
import crypto from 'crypto';
export default {
plugins: [
zodCodepen({
transform(code, id) {
// 为每个模式生成哈希值
const hash = crypto
.createHash('md5')
.update(code)
.digest('hex')
.substring(0, 8);
return `/* Schema version: ${hash} */\n${code}`;
}
})
]
};高级用法
自定义转换器
创建自定义转换逻辑:
typescript
// vite.config.ts
import zodCodepen from '@zod-codepen/vite-plugin';
export default {
plugins: [
zodCodepen({
transform(code, id) {
// 只转换特定的导出
if (id.includes('internal')) {
return null; // 跳过内部模式
}
// 添加自定义注释
return `/**
* Auto-generated from ${id}
* @generated ${new Date().toISOString()}
*/
${code}`;
}
})
]
};与其他插件集成
与其他 Vite 插件协同工作:
typescript
import { defineConfig } from 'vite';
import zodCodepen from '@zod-codepen/vite-plugin';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
// 先转换模式
zodCodepen({
include: 'src/**/*.schema.ts',
}),
// 然后生成类型声明
dts({
include: ['src/**/*.ts'],
}),
],
});条件转换
根据环境变量控制转换行为:
typescript
import zodCodepen from '@zod-codepen/vite-plugin';
export default {
plugins: [
zodCodepen({
// 仅在生产构建时启用
enableInDev: false,
// 根据环境变量决定包含哪些文件
include: process.env.INCLUDE_INTERNAL
? ['src/**/*.schema.ts']
: ['src/public/**/*.schema.ts'],
})
]
};性能优化
1. 缓存机制
插件自动缓存转换结果以提高构建性能:
typescript
zodCodepen({
// 缓存配置(默认启用)
cache: {
enabled: true,
directory: 'node_modules/.vite/zod-codepen',
}
})2. 并行处理
对于大型项目,启用并行处理:
typescript
zodCodepen({
// 使用 Worker 线程进行并行转换
parallel: true,
// 最大并行数
maxWorkers: 4,
})3. 选择性转换
只转换真正需要的模式:
typescript
// 使用查询参数控制转换
import { UserSchema } from './schemas/user?codepen';
import { PostSchema } from './schemas/post'; // 不转换
// 或使用动态导入
const schemas = await Promise.all([
import('./schemas/user?codepen'),
import('./schemas/post?codepen'),
]);调试
启用调试输出
typescript
zodCodepen({
debug: true, // 输出详细的转换日志
})查看转换结果
typescript
// 在开发服务器中查看转换结果
import { UserSchema } from './schemas/user?codepen&raw';
console.log(UserSchema); // 原始转换输出最佳实践
1. 文件组织
推荐的项目结构:
src/
├── schemas/ # 原始 Zod 模式
│ ├── user.schema.ts
│ ├── post.schema.ts
│ └── index.ts
├── generated/ # 生成的代码
│ └── schemas.ts
└── validators/ # 验证逻辑
└── index.ts2. 命名约定
- 使用
.schema.ts后缀标识模式文件 - 导出名称使用 PascalCase + Schema 后缀
- 生成的文件使用
.generated.ts后缀
3. 类型安全
确保生成的代码保持类型安全:
typescript
// schemas/user.schema.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
});
// 导出类型定义
export type User = z.infer<typeof UserSchema>;typescript
// 使用时保持类型
import type { User } from './schemas/user.schema';
import { UserSchema } from './schemas/user.schema?codepen';
function processUser(user: User) {
// 类型安全的操作
}故障排除
常见问题
Q: 转换后的代码无法执行?
A: 确保你的运行时环境中有 Zod 可用:
typescript
// 确保 Zod 在全局作用域可用
window.z = z; // 浏览器环境
global.z = z; // Node.js 环境Q: HMR (热模块替换) 不工作?
A: 在开发模式下启用插件:
typescript
zodCodepen({
enableInDev: true,
})Q: 构建时间过长?
A: 优化包含/排除模式,只转换必要的文件:
typescript
zodCodepen({
include: ['src/schemas/**/*.schema.ts'],
exclude: ['**/*.test.ts', '**/*.spec.ts'],
})迁移指南
从手动序列化迁移
如果你之前手动使用 serialize() 函数:
typescript
// 之前
import { serialize } from '@zod-codepen/zod-v3';
import { UserSchema } from './schemas';
const code = serialize(UserSchema);typescript
// 现在
import { UserSchema } from './schemas/user?codepen';
// code 已经自动生成从其他构建工具迁移
如果你从 Webpack 或 Rollup 迁移:
typescript
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.schema\.ts$/,
use: 'zod-codepen-loader', // 假设的 loader
}
]
}
};typescript
// vite.config.ts
import zodCodepen from '@zod-codepen/vite-plugin';
export default {
plugins: [
zodCodepen({
include: '**/*.schema.ts',
})
]
};