AI 驱动的 Vue3 应用开发平台 深入探究(十七):扩展与定制之扩展 Provider 系统

2 阅读6分钟

扩展 Provider 系统

Provider System 是 VTJ 的架构骨干,负责编排项目管理、文件操作、数据持久化和运行时能力。该系统允许你通过自定义 Service 实现和基于 UMD 的扩展模块来扩展平台功能,从而与你的基础设施和业务需求实现无缝集成。

架构概述

Provider System 通过分层架构运行,其中抽象的 Service 协议定义契约,具体实现提供存储和 API 能力,而 Provider 类编排运行时行为。扩展作为 UMD 模块动态加载,使自定义服务、组件和安装钩子能够增强设计器的能力。

graph TD
    subgraph Extension Layer
        A[UMD Extension Module] --> B[Extension Factory] --> C[ExtensionOutput]
    end
    subgraph Service Layer
        D[Service Abstract] --> E[BaseService]
        D --> F[LocalService]
        D --> G[StorageService]
        D --> H[CustomService]
    end
    subgraph Provider Layer
        I[Provider] --> J[Engine]
        I --> K[Asset Management]
        I --> L[Router Integration]
        I --> M[Mock API System]
    end
    C --> I
    E --> I
    F --> I
    G --> I
    H --> I

理解 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 实现,你可以扩展它们或直接使用:

实现基类存储后端使用场景
BaseServiceServiceHTTP API具有远程服务器的生产环境部署
LocalServiceBaseService内存无持久化的开发和测试
StorageServiceBaseService浏览器 LocalStorage用于演示/原型的客户端持久化

创建自定义 Service 实现

要使用自定义业务逻辑扩展 Provider System,请创建一个扩展内置实现之一的新 Service 类。此方法非常适合与现有的后端 API、数据库或云服务集成。

步骤 1:定义自定义 Service 类

创建一个新文件(例如 CustomService.ts)并扩展适当的基类:

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 配置和任何附加参数:

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 格式:

// 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 属性进行配置,并在引擎初始化期间动态加载。

扩展加载流程

graph TD
    A[Engine Initialization] --> B[Load VTJConfig]
    B --> C[Create Extension Instance]
    C --> D[Extension.load Method Called]
    D --> E{Has Library Config?}
    E -- Yes --> F[Load UMD Module]
    E -- No --> G[Return Default Options]
    F --> H{Module is Function?}
    H -- Yes --> I[Execute Factory Function<br>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 中的扩展配置指定了如何加载和配置你的自定义扩展:

interface VTJConfig {
  extension?: {
    urls: string[]; // UMD 模块文件的路径
    library: string; // 全局导出名称
    params?: Array<Record<string, any>>; // 附加参数
  };
  __BASE_PATH__?: string; // 解析 URL 的基本路径
  // ... 其他 VTJConfig 属性
}

💡 VTJConfig 中的 __BASE_PATH__ 属性对于正确解析扩展 URL 至关重要。它通常对应于你的应用程序部署路径,并且当你的应用部署在子目录(例如 /dashboard/ 而非 /)中时必须设置。

在应用程序中配置扩展

要集成你的自定义扩展,请在应用程序的入口点对其进行配置:

// 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:

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 或扩展中重写 authaccess 配置:

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 以与第三方服务交互:

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,它允许应用程序增强功能,可以通过你的扩展进行配置:

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"],
  },
});

依赖和物料加载

你的扩展可以自定义依赖项和物料的加载方式:

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/", // 重写默认物料路径
});

测试和调试扩展

有效地开发扩展需要适当的测试策略和调试技术。

本地开发设置

  1. 创建本地扩展项目结构:
my-vtj-extension/
├── src/
│   ├── index.ts          │   ├── CustomService.ts  # 自定义服务实现
│   └── components/       # 自定义组件
├── vite.config.ts       # UMD 构建配置
└── package.json
  1. 本地构建和提供服务:
// vite.config.ts
export default defineConfig({
  build: {
    lib: {
      entry: "src/index.ts",
      name: "MyVTJExtension",
      fileName: "extension",
      formats: ["umd"],
    },
  },
  server: {
    port: 3001,
    cors: true, // 为本地开发启用 CORS
  },
});

调试技术

添加控制台日志以跟踪扩展加载:

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 时,请遵循这些最佳实践以确保可维护性和兼容性:

实践描述示例
扩展基类始终扩展 BaseServiceLocalServiceStorageServiceclass CustomService extends BaseService
最小化重写仅重写需要自定义逻辑的方法仅为了验证而重写 saveFile()
错误处理实现强大的错误处理和回退带有用户友好消息的 try-catch
类型安全利用 TypeScript 接口处理所有自定义数据使用 ProjectSchemaBlockSchema 类型
版本兼容性在你的扩展中固定 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 与现有的后端系统、身份验证提供商和部署工作流无缝集成,同时保持完整的类型安全和运行时兼容性。

参考资料