插件
VuReact 提供了一个灵活的插件系统,允许你在编译过程的不同阶段注入自定义逻辑。插件可以用于增强功能、添加元数据、修改输出结果等。
插件系统概述
VuReact 的编译过程分为三个阶段,每个阶段都支持插件:
- 解析阶段 (Parse) - 将 Vue SFC 源码解析为结构化 AST
- 转换阶段 (Transform) - 将 Vue AST 转换为 React 中间表示 (IR)
- 代码生成阶段 (Codegen) - 将 React IR 生成为最终的 JSX/TSX 代码
- 编译阶段 (Compilation) - 已经处理好的,带有文件路径的结果
每个阶段都有对应的插件接口,你可以根据需要选择在哪个阶段注入逻辑。
插件类型定义
基础插件接口
typescript
interface PluginRegister<T> {
[name: string]: (result: T, ctx: ICompilationContext) => void;
}各阶段插件类型
typescript
// 解析阶段插件
type ParserPlugin = PluginRegister<ParseResult>;
// 转换阶段插件
type TransformerPlugin = PluginRegister<ReactIRDescriptor>;
// 代码生成阶段插件
type CodegenPlugin = PluginRegister<GeneratorResult>;
// 编译完成插件(在所有阶段之后执行)
type CompilationPlugin = PluginRegister<CompilationResult>;配置插件
在 vureact.config.js 中配置插件:
javascript
import { defineConfig } from '@vureact/compiler-core';
export default defineConfig({
plugins: {
// 解析阶段插件
parser: {
extractMetadata: (result, ctx) => {
// 在解析阶段添加自定义逻辑
},
},
// 转换阶段插件
transformer: {
analyzeDependencies: (result, ctx) => {
// 在转换阶段添加自定义逻辑
},
},
// 代码生成阶段插件
codegen: {
formatOutput: (result, ctx) => {
// 在代码生成阶段添加自定义逻辑
},
},
// 编译完成插件(在所有阶段之后执行)
customAnalytics: (result, ctx) => {
// 编译完成后执行
},
},
});插件上下文
每个插件都会接收两个参数:
result- 当前阶段的处理结果ctx- 编译上下文对象
编译上下文接口
typescript
interface ICompilationContext {
fileId: string;: string; // 文件 id
filename: string; // 文件名
compName: string; // 组件名
imports: Map<string, ImportItem[]>; // 收集需要导入的 React、VuReact 运行时模块
source: string; // 源代码
inputType: 'sfc' | 'script-ts' | 'script-js'; // 输入类型
route?: boolean; // 是否使用了路由
// ... 其他上下文信息
}各阶段插件示例
1. 解析阶段插件
解析阶段插件可以访问 Vue SFC 的完整解析结果:
typescript
// 使用插件
export default defineConfig({
plugins: {
parser: {
// 示例:提取组件元数据
extractComponentInfo: (result: ParseResult, ctx: ICompilationContext) => {
const { template, script, style } = result;
// 提取组件名称
const componentName = extractNameFromScript(script);
// 分析模板复杂度
const templateComplexity = analyzeTemplate(template);
// 检查样式类型
const hasScopedStyle = style?.source?.scoped || false;
const hasCSSModules = style?.source?.module || false;
// 将元数据添加到结果中
(result as any).metadata = {
componentName,
templateComplexity,
hasScopedStyle,
hasCSSModules,
analyzedAt: new Date().toISOString(),
};
},
},
},
});2. 转换阶段插件
转换阶段插件可以访问 React 中间表示:
typescript
export default defineConfig({
plugins: {
transformer: {
// 示例:分析依赖关系
analyzeDependencies: (result: ReactIRDescriptor, ctx: ICompilationContext) => {
const { script, template } = result;
// 从脚本中提取导入语句
const imports = extractImports(script);
// 从模板中提取使用的组件
const usedComponents = extractComponentsFromTemplate(template);
// 分析运行时依赖
const runtimeDeps = analyzeRuntimeDependencies(script);
// 添加依赖分析结果
(result as any).dependencies = {
imports,
usedComponents,
runtimeDeps,
totalDeps: imports.length + runtimeDeps.length,
};
},
// 示例:样式预处理
processStyles: (result: ReactIRDescriptor, ctx: ICompilationContext) => {
if (result.style) {
// 添加 CSS 前缀
result.style = addVendorPrefixes(result.style);
// 压缩 CSS
result.style = minifyCSS(result.style);
// 添加源映射注释
result.style += `\n/* Source: ${ctx.filename} */`;
}
},
},
},
});3. 代码生成阶段插件
代码生成阶段插件可以修改最终的输出代码:
typescript
export default defineConfig({
plugins: {
codegen: {
// 示例:代码格式化
prettify: (result: GeneratorResult, ctx: ICompilationContext) => {
// 使用 prettier 格式化代码(vureact)
const formatted = prettier.format(result.code, {
parser: 'babel',
semi: true,
singleQuote: true,
trailingComma: 'es5',
});
result.code = formatted;
},
// 示例:添加文件头注释
addFileHeader: (result: GeneratorResult, ctx: ICompilationContext) => {
const header = `/** Generated by VuReact */`;
result.code = header + result.code;
},
// 示例:代码质量检查
lintGeneratedCode: (result: GeneratorResult, ctx: ICompilationContext) => {
const issues = eslint.verify(result.code, {
rules: {
'react/react-in-jsx-scope': 'off',
'no-unused-vars': 'warn',
'no-console': 'warn',
},
});
if (issues.length > 0) {
console.warn(`Found ${issues.length} lint issues in ${ctx.filename}:`);
issues.forEach((issue) => {
console.warn(` ${issue.line}:${issue.column} ${issue.message}`);
});
}
},
},
},
});4. 编译完成插件
编译完成插件在所有阶段之后执行,可以访问完整的编译结果:
typescript
export default defineConfig({
plugins: {
// 直接添加插件函数
// 示例:编译统计
collectStats: (result: CompilationResult, ctx: ICompilationContext) => {
const stats = {
filename: ctx.filename,
fileSize: ctx.source.length,
generatedSize: result.code.length,
hasCSS: !!result.fileInfo.css,
hasJSX: !!result.fileInfo.jsx,
hasScript: !!result.fileInfo.script,
};
// 保存统计信息
saveCompilationStats(stats);
// 添加到结果中
(result as any).stats = stats;
},
// 示例:发送通知
sendNotification: (result: CompilationResult, ctx: ICompilationContext) => {
if (process.env.NODE_ENV === 'development') {
// 开发环境发送桌面通知
new Notification('VuReact Compilation Complete', {
body: `Successfully compiled ${ctx.filename}`,
icon: '/path/to/icon.png',
});
}
// 发送到监控系统
sendToMonitoringSystem({
event: 'compilation_complete',
data: (result as any).stats
timestamp: new Date().toISOString(),
});
},
},
});实用插件示例
注意,仅作为参考,请勿直接使用。
1. 性能分析插件
typescript
const performancePlugin = {
measurePerformance: (result: any, ctx: ICompilationContext) => {
if (!ctx.metrics) ctx.metrics = {};
const stage = ctx.currentStage; // 'parse', 'transform', 'codegen'
const duration = Date.now() - ctx.stageStartTime;
ctx.metrics[stage] = duration;
if (stage === 'codegen') {
// 所有阶段完成,输出性能报告
console.log(`
VuReact Performance Report for ${ctx.filename}:
Parse: ${ctx.metrics.parse}ms
Transform: ${ctx.metrics.transform}ms
Codegen: ${ctx.metrics.codegen}ms
Total: ${Object.values(ctx.metrics).reduce((a, b) => a + b, 0)}ms
`);
}
},
};
// 在所有阶段都使用同一个插件
export default defineConfig({
plugins: {
parser: { performancePlugin },
transformer: { performancePlugin },
codegen: { performancePlugin },
},
});2. 依赖分析插件
typescript
const dependencyGraphPlugin = {
buildDependencyGraph: (result: CompilationResult, ctx: ICompilationContext) => {
const graph = {
nodes: [],
edges: [],
};
// 分析导入语句
const imports = extractImportsFromCode(result.code);
imports.forEach((imp) => {
graph.nodes.push({
id: imp.source,
type: 'external',
label: imp.source,
});
graph.edges.push({
source: ctx.filename,
target: imp.source,
type: 'import',
});
});
// 保存依赖图
saveDependencyGraph(graph);
// 添加到结果中
(result as any).dependencyGraph = graph;
},
};3. 代码转换插件
typescript
const codeTransformationPlugin = {
transformImports: (result: GeneratorResult, ctx: ICompilationContext) => {
// 将 Vue 特定的导入转换为 React 等效
const transformations = {
vue: '@vureact/runtime-core',
'@vue/composition-api': '@vureact/runtime-core',
'vue-router': 'react-router-dom',
};
let code = result.code;
Object.entries(transformations).forEach(([from, to]) => {
const regex = new RegExp(`from ['"]${from}['"]`, 'g');
code = code.replace(regex, `from '${to}'`);
});
result.code = code;
},
addPolyfills: (result: GeneratorResult, ctx: ICompilationContext) => {
// 为旧浏览器添加 polyfill 导入
if (needsPolyfills(ctx.browserTarget)) {
const polyfillImport = "import 'core-js/stable';\nimport 'regenerator-runtime/runtime';\n\n";
result.code = polyfillImport + result.code;
}
},
};插件开发最佳实践
1. 错误处理
typescript
const safePlugin = {
myPlugin: (result: any, ctx: ICompilationContext) => {
try {
// 插件逻辑
performComplexOperation(result);
} catch (error) {
// 优雅地处理错误,不影响编译过程
console.warn(`Plugin myPlugin failed: ${error.message}`);
// 可以添加错误信息到结果中
if (!result.errors) result.errors = [];
result.errors.push({
plugin: 'myPlugin',
message: error.message,
timestamp: new Date().toISOString(),
});
}
},
};2. 性能优化
typescript
const optimizedPlugin = {
efficientPlugin: (result: any, ctx: ICompilationContext) => {
// 使用缓存避免重复计算
if (!ctx.cache) ctx.cache = new Map();
const cacheKey = `plugin-${ctx.filename}`;
if (ctx.cache.has(cacheKey)) {
return ctx.cache.get(cacheKey);
}
// 昂贵的计算
const computedValue = expensiveComputation(result);
ctx.cache.set(cacheKey, computedValue);
return computedValue;
},
};3. 配置化插件
typescript
// 创建可配置的插件工厂
function createConfigurablePlugin(options = {}) {
return {
configurablePlugin: (result: any, ctx: ICompilationContext) => {
const { enabled = true, logLevel = 'info', transformRules = {} } = options;
if (!enabled) return;
// 使用配置
if (logLevel === 'debug') {
console.debug('Plugin executing for:', ctx.filename);
}
// 应用转换规则
applyTransformRules(result, transformRules);
},
};
}
// 使用配置化插件
export default defineConfig({
plugins: {
...createConfigurablePlugin({
enabled: process.env.NODE_ENV === 'development',
logLevel: 'debug',
transformRules: {
vue: '@vureact/runtime-core',
},
}),
},
});插件执行顺序
插件按照配置的顺序执行:
- 解析阶段插件(
parser) - 转换阶段插件(
transformer) - 代码生成阶段插件(
codegen) - 编译完成插件(直接配置在
plugins下的插件)
javascript
export default defineConfig({
plugins: {
// 这些插件在所有阶段之后执行
pluginA: () => console.log('最后执行'),
pluginB: () => console.log('最后执行'),
parser: {
// 解析阶段插件
plugin1: () => console.log('第一阶段执行'),
plugin2: () => console.log('第一阶段执行'),
},
transformer: {
// 转换阶段插件
plugin3: () => console.log('第二阶段执行'),
},
codegen: {
// 代码生成阶段插件
plugin4: () => console.log('第三阶段执行'),
},
},
});调试插件
1. 使用日志
typescript
const debugPlugin = {
debugPlugin: (result: any, ctx: ICompilationContext) => {
console.log('=== Plugin Debug Info ===');
console.log('Filename:', ctx.filename);
console.log('Stage:', ctx.currentStage);
console.log('Result type:', result.constructor.name);
console.log('Result keys:', Object.keys(result));
console.log('========================');
},
};2. 开发工具
typescript
// 插件开发助手
const pluginDevTools = {
inspectResult: (result: any, ctx: ICompilationContext) => {
if (process.env.VUREACT_PLUGIN_DEBUG) {
// 将结果保存到文件以便分析
const fs = require('fs');
const path = require('path');
const debugDir = path.join(ctx.root, '.vureact-debug');
if (!fs.existsSync(debugDir)) {
fs.mkdirSync(debugDir, { recursive: true });
}
const debugFile = path.join(
debugDir,
`${path.basename(ctx.filename)}-${ctx.currentStage}.json`,
);
fs.writeFileSync(debugFile, JSON.stringify(result, null, 2));
console.log(`Debug info saved to: ${debugFile}`);
}
},
};注意事项
- 不要修改原始对象:尽量创建新对象或添加新属性,避免修改插件系统的内部状态
- 保持插件轻量:插件会在每个文件编译时执行,避免昂贵的操作
- 处理异步操作:插件目前是同步的,如果需要异步操作,需要在插件内部处理
- 错误边界:确保插件错误不会导致整个编译过程失败
- 兼容性:考虑不同版本 VuReact 的兼容性,避免使用内部 API
下一步
- 参考 API 了解完整的类型定义
