扩展 Provider 系统
Provider System 是 VTJ 的架构骨干,负责编排项目管理、文件操作、数据持久化和运行时能力。该系统允许你通过自定义 Service 实现和基于 UMD 的扩展模块来扩展平台功能,从而与你的基础设施和业务需求实现无缝集成。
架构概述
Provider System 通过分层架构运行,其中抽象的 Service 协议定义契约,具体实现提供存储和 API 能力,而 Provider 类编排运行时行为。扩展作为 UMD 模块动态加载,使自定义服务、组件和安装钩子能够增强设计器的能力。
理解 Service 协议
Service 抽象类定义了所有 Provider 操作的完整接口。它包含项目生命周期、文件管理、历史记录跟踪、代码生成、静态文件处理和插件物料集成。所有 Service 实现都必须扩展此抽象类,以确保在不同存储后端之间行为一致。
核心 Service 方法
| 方法类别 | 关键方法 | 用途 |
|---|---|---|
| 项目管理 | init(), saveProject(), publish(), genSource() |
初始化、持久化和导出项目配置 |
| 文件操作 | saveFile(), getFile(), removeFile(), publishFile() |
管理区块和页面模式的 CRUD 操作 |
| 历史管理 | saveHistory(), getHistory(), getHistoryItem(), saveHistoryItem() |
跟踪和检索撤销/重做状态 |
| 代码生成 | genVueContent(), parseVue(), createRawPage(), removeRawPage() |
双向 DSL-Vue 转换 |
| 静态资源 | uploadStaticFile(), getStaticFiles(), removeStaticFile() |
处理媒体和资源上传 |
| 扩展支持 | getPluginMaterial(), getExtension() |
检索插件物料和扩展配置 |
内置 Service 实现
VTJ 提供了三种标准 Service 实现,你可以扩展它们或直接使用:
| 实现 | 基类 | 存储后端 | 使用场景 |
|---|---|---|---|
| BaseService | Service | HTTP API | 具有远程服务器的生产环境部署 |
| LocalService | BaseService | 内存 | 无持久化的开发和测试 |
| StorageService | BaseService | 浏览器 LocalStorage | 用于演示/原型的客户端持久化 |
创建自定义 Service 实现
要使用自定义业务逻辑扩展 Provider System,请创建一个扩展内置实现之一的新 Service 类。此方法非常适合与现有的后端 API、数据库或云服务集成。
步骤 1:定义自定义 Service 类
创建一个新文件(例如 CustomService.ts)并扩展适当的基类:
typescript
import { BaseService } from "@vtj/renderer";
import type {
ProjectSchema,
BlockSchema,
VTJConfig,
MaterialDescription,
} from "@vtj/core";
export class CustomService extends BaseService {
private apiBaseUrl: string;
constructor(apiBaseUrl: string = "https://api.example.com/vtj") {
super();
this.apiBaseUrl = apiBaseUrl;
}
// 重写 getExtension 以提供自定义配置
async getExtension(): Promise<VTJConfig | undefined> {
return {
platform: "web",
history: "hash",
remote: this.apiBaseUrl,
auth: async () => {
// 实现自定义身份验证逻辑
const token = localStorage.getItem("auth_token");
if (!token) {
// 重定向到登录或刷新 token
}
return { token };
},
access: {
// 定义访问控制规则
},
};
}
// 重写 init 以丰富项目数据
async init(project: Partial<ProjectSchema>): Promise<ProjectSchema> {
const initialized = await super.init(project);
// 添加自定义初始化逻辑
// 例如:获取用户权限、加载模板等。
return initialized;
}
// 重写 saveFile 以添加验证或转换
async saveFile(file: BlockSchema): Promise<boolean> {
// 添加验证逻辑
if (!file.name || !file.id) {
throw new Error("File must have name and id");
}
// 在保存之前转换或丰富数据
return super.saveFile(file);
}
// 特定于业务的自定义方法
async validateProject(project: ProjectSchema): Promise<boolean> {
// 实现特定于项目的验证规则
return true;
}
}
步骤 2:创建扩展模块
扩展模块必须导出一个返回 ExtensionOutput 的默认函数。该函数接收 VTJ 配置和任何附加参数:
typescript
import type { App } from "vue";
import type { ExtensionOutput, VTJConfig, Engine } from "@vtj/pro";
import * as __VTJ_PRO__ from "@vtj/pro";
import { CustomService } from "./CustomService";
const service = new CustomService("https://your-api.com/vtj");
export default (_config: VTJConfig, ..._args: any[]) => {
const options: ExtensionOutput = {
service,
install(app: App, engine?: Engine) {
// 自定义安装逻辑
// 注册全局组件、指令或插件
app.config.globalProperties.$customAPI = {
// 向你的应用公开自定义 API
};
},
};
return options;
};
步骤 3:构建为 UMD 模块
配置你的构建以输出可动态加载的 UMD 格式:
typescript
// vite.config.ts or build.config.ts
import { defineConfig } from "vite";
export default defineConfig({
build: {
lib: {
entry: "src/index.ts",
name: "VTJExtension", // 配置中引用的库名称
fileName: "extension",
formats: ["umd"],
},
rollupOptions: {
external: [
"vue",
"@vtj/core",
"@vtj/designer",
"@vtj/renderer",
"@vtj/utils",
],
output: {
globals: {
vue: "Vue",
"@vtj/core": "__VTJ_PRO__",
"@vtj/designer": "__VTJ_PRO__",
"@vtj/renderer": "__VTJ_PRO__",
"@vtj/utils": "__VTJ_PRO__",
},
},
},
},
});
扩展系统深度解析
VTJ 中的扩展加载机制提供了一种灵活的、运行时驱动的方法来扩展 Provider System。扩展通过 VTJConfig.extension 属性进行配置,并在引擎初始化期间动态加载。
扩展加载流程
with config & args] H -- No --> J[Use Module as Options] I --> K[Merge with Engine Options] J --> K K --> L[Initialize Provider] L --> M[Install Extension Hooks] M --> N[Engine Ready] G --> K
VTJConfig 扩展模式
VTJConfig 中的扩展配置指定了如何加载和配置你的自定义扩展:
typescript
interface VTJConfig {
extension?: {
urls: string[]; // UMD 模块文件的路径
library: string; // 全局导出名称
params?: Array<Record<string, any>>; // 附加参数
};
__BASE_PATH__?: string; // 解析 URL 的基本路径
// ... 其他 VTJConfig 属性
}
💡 VTJConfig 中的
__BASE_PATH__属性对于正确解析扩展 URL 至关重要。它通常对应于你的应用程序部署路径,并且当你的应用部署在子目录(例如/dashboard/而非/)中时必须设置。
在应用程序中配置扩展
要集成你的自定义扩展,请在应用程序的入口点对其进行配置:
typescript
// main.ts 或设计器初始化
import { createDesigner } from "@vtj/designer";
const designer = createDesigner({
container: document.getElementById("designer-container"),
service: new CustomService(), // 可选:直接注入 service
// 扩展加载配置
remote: "https://your-server.com",
extension: {
library: "VTJExtension",
urls: ["/assets/extension.js"],
params: [
{ theme: "dark", locale: "zh-CN" }, // 自定义参数
],
},
// ... 其他选项
});
高级扩展模式
除了基本的 Service 替换之外,扩展系统还支持多种高级模式,用于深度自定义 Provider System。
使用自定义组件和 Setter 进行扩展
你可以通过扩展模块注册自定义组件和属性 setter:
typescript
import * as __VTJ_PRO__ from "@vtj/pro";
const { widgetManager, setterManager } = __VTJ_PRO__;
// 注册自定义组件
widgetManager.set("CustomDataPicker", {
component: () => import("./components/CustomDataPicker.vue"),
props: {
apiKey: "your-api-key",
},
});
// 注册自定义 setter
setterManager.set("color-picker", () => import("./setters/ColorSetter.vue"));
export default (config: VTJConfig) => ({
service: new CustomService(),
install(app: App, engine?: Engine) {
// 在运行时进行额外的组件注册
widgetManager.set("DynamicWidget", {
component: {
template: "<div>{{ data.value }}</div>",
setup() {
return { data: reactive({ value: "Hello" }) };
},
},
});
},
});
实现自定义身份验证和访问控制
在你的 Service 或扩展中重写 auth 和 access 配置:
typescript
async getExtension(): Promise<VTJConfig> {
return {
auth: async () => {
// 实现 OAuth2 或 JWT 身份验证
const response = await fetch('/api/auth/validate', {
headers: {
'Authorization': `Bearer ${getStoredToken()}`
}
});
if (!response.ok) {
window.location.href = '/login';
return null;
}
return await response.json();
},
access: {
// 定义基于角色的访问控制
admin: {
canEdit: true,
canPublish: true,
canDelete: true
},
developer: {
canEdit: true,
canPublish: false,
canDelete: false
}
}
};
}
与外部 API 和服务集成
扩展你的 Service 以与第三方服务交互:
typescript
export class CloudIntegratedService extends BaseService {
private cloudClient: CloudServiceClient;
constructor(apiKey: string) {
super();
this.cloudClient = new CloudServiceClient(apiKey);
}
// 重写 publish 以部署到云平台
async publish(project: ProjectSchema): Promise<boolean> {
try {
// 生成 Vue 代码
const code = await this.genVueContent(project, project.pages[0].dsl);
// 部署到云端
const deployment = await this.cloudClient.deploy({
name: project.name,
files: [{ path: "index.vue", content: code }],
});
return deployment.success;
} catch (error) {
console.error("Cloud deployment failed:", error);
return false;
}
}
// 云特定操作的自定义方法
async syncCloudPages(projectId: string): Promise<BlockSchema[]> {
const pages = await this.cloudClient.listPages(projectId);
return pages.map((page) => this.mapToBlockSchema(page));
}
}
Provider System 运行时集成
渲染器中的 Provider 类管理你的自定义 Service 和扩展配置的运行时集成。它处理依赖加载、资源管理、路由集成和 Mock API 系统初始化。
Provider 选项和扩展集成
Provider 接受一个 EnhanceConfig,它允许应用程序增强功能,可以通过你的扩展进行配置:
typescript
interface EnhanceConfig {
name: string;
urls: string[]; // 要加载的额外 JavaScript/CSS
}
// 在你的扩展中
export default (config: VTJConfig) => ({
service: new CustomService(),
enhance: {
name: "my-enhance",
urls: ["/assets/custom-theme.css", "/libs/analytics.js"],
},
});
依赖和物料加载
你的扩展可以自定义依赖项和物料的加载方式:
typescript
export default (config: VTJConfig) => ({
service: new CustomService(),
dependencies: {
// 重写默认依赖加载
"element-plus": () => import("element-plus"),
"lodash-es": () => import("lodash-es"),
},
materials: {
// 自定义物料加载逻辑
"my-custom-lib": () => import("my-custom-lib/dist/index.umd"),
},
libraryOptions: {
// 配置特定于库的选项
echarts: { locale: "zh-CN" },
},
materialPath: "/custom-materials/", // 重写默认物料路径
});
测试和调试扩展
有效地开发扩展需要适当的测试策略和调试技术。
本地开发设置
- 创建本地扩展项目结构:
perl
my-vtj-extension/
├── src/
│ ├── index.ts │ ├── CustomService.ts # 自定义服务实现
│ └── components/ # 自定义组件
├── vite.config.ts # UMD 构建配置
└── package.json
- 本地构建和提供服务:
typescript
// vite.config.ts
export default defineConfig({
build: {
lib: {
entry: "src/index.ts",
name: "MyVTJExtension",
fileName: "extension",
formats: ["umd"],
},
},
server: {
port: 3001,
cors: true, // 为本地开发启用 CORS
},
});
调试技术
添加控制台日志以跟踪扩展加载:
typescript
export default (config: VTJConfig, ...args: any[]) => {
console.log("Extension loading with config:", config);
console.log("Extension arguments:", args);
const options: ExtensionOutput = {
service: new CustomService(),
install(app: App, engine?: Engine) {
console.log("Extension install called");
console.log("Vue app instance:", app);
console.log("Engine instance:", engine);
},
};
console.log("Extension output:", options);
return options;
};
使用浏览器 DevTools 检查已加载的模块,并验证你的 UMD 库是否在配置的 library 名称下正确暴露给全局作用域。
迁移和最佳实践
扩展 Provider System 时,请遵循这些最佳实践以确保可维护性和兼容性:
| 实践 | 描述 | 示例 |
|---|---|---|
| 扩展基类 | 始终扩展 BaseService、LocalService 或 StorageService |
class CustomService extends BaseService |
| 最小化重写 | 仅重写需要自定义逻辑的方法 | 仅为了验证而重写 saveFile() |
| 错误处理 | 实现强大的错误处理和回退 | 带有用户友好消息的 try-catch 块 |
| 类型安全 | 利用 TypeScript 接口处理所有自定义数据 | 使用 ProjectSchema、BlockSchema 类型 |
| 版本兼容性 | 在你的扩展中固定 VTJ 包版本 | 在 package.json 中使用特定版本范围 |
💡 始终在你的自定义 Service 中实现
getExtension()方法,即使返回默认配置。这确保了你的扩展可以提供正常操作所需的任何必要 VTJConfig 重写。
后续步骤
扩展 Provider System 后,你可能想要探索:
- Provider API Reference - 所有 Service 方法和 Provider 选项的完整 API 文档
- Engine API Reference - 关于 Engine 类及其与 Providers 交互的详细信息
- Plugin System Development - 了解如何创建扩展设计器功能的插件
- Integrating Third-Party Libraries - 关于合并外部依赖项和库的指南
Provider System 为自定义 VTJ 以满足你的特定基础设施需求提供了强大、可扩展的基础。通过实现自定义 Services 和扩展,你可以将 VTJ 与现有的后端系统、身份验证提供商和部署工作流无缝集成,同时保持完整的类型安全和运行时兼容性。
参考资料
- 开源代码仓库:gitee.com/newgateway/...