AI 驱动的 Vue3 应用开发平台 深入探究(十三):物料系统之区块与页面模板

31 阅读9分钟

区块与页面模板

块模板和页面模板构成了 VTJ 用于创建可复用 UI 组件和完整页面布局的抽象层。该系统使开发者能够设计、管理和分发预构建的 UI 解决方案,支持本地块管理和远程模板市场的集成。

模板系统架构

模板系统通过分层架构运行,该架构连接了设计器界面、核心引擎和远程市场服务。

graph TD
    subgraph "Template Types"
        ST[Schema - Designed]
        UST[UrlSchema - Referenced]
        PT[Plugin - Plugin Library]
    end

    subgraph "Designer Layer"
        BW[Blocks Widget]
        TW[Templates Widget]
        UB[useBlocks Hook]
        UT[useTemplates Hook]
    end

    subgraph "Core Engine Layer"
        ENG[Engine]
        PM[ProjectModel]
        BM[BlockModel]
    end

    subgraph "Data Layer"
        LS[Local Storage]
        RMS[Remote Marketplace Service]
    end

    ST --> BW
    UST --> BW
    PT --> BW
    BW --> UB
    TW --> UT
    UB --> ENG
    UT --> ENG
    ENG --> PM
    PM --> BM
    BM --> LS
    UT --> RMS
    RMS -.-> UT

这种架构支持双向流动:模板可以从市场安装到本地块,本地块也可以被设计、引用或从插件库获取。

块模板概述

块模板是可复用的 UI 组件,在 VTJ 中存在于三个抽象级别。每个块由 BlockFile 接口表示,包含元数据和 DSL 内容,通过 ProjectModel 管理,并实例化为 BlockModel 实例。

块文件结构

块文件包含定义其身份和来源的基本元数据:

interface BlockFile {
  type: FileType; // 'block'
  id: string; // 唯一标识符
  name: string; // 块名称
  title: string; // 显示标题
  category?: string; // 分组类别
  market?: MarketInstallInfo; // 市场安装信息
  fromType?: "Schema" | "UrlSchema" | "Plugin";
  preset?: boolean; // 是否为预设插件
  urls?: string; // Plugin/UrlSchema 的资源 URL
  library?: string; // 插件库名称
  dsl?: BlockSchema; // 块 DSL 内容
}

fromType 属性决定了块内容的来源和管理方式。块创建后此字段不可更改,以确保内容来源的完整性。

块类型及其特性

VTJ 支持三种不同的块类型,每种类型服务于开发工作流中的不同用例。

类型描述URL 格式可编辑用例
Schema在 VTJ 设计器内设计的块从头创建的自定义组件
UrlSchema从外部 JSON 引用的块单个 JSON URL通过 CDN 或外部存储共享的块
Plugin来自插件库的块多个 (.css,.js) URL否(除非非预设)第三方组件库

类型区别会影响块的渲染、编辑和分发方式。标记为 preset: true 的插件块无法编辑或删除,以保护第三方组件的完整性。

块模式结构

块的实际内容通过 BlockSchema 定义,其中包括组件定义、状态管理和生命周期钩子。

classDiagram
    class BlockSchema {
        +id: string
        +name: string
        +locked: boolean
        +inject: BlockInject[]
        +state: BlockState
        +lifeCycles: object
        +methods: object
        +computed: object
        +watch: BlockWatch[]
        +css: string
        +props: Array
        +emits: Array
        +expose: string[]
        +slots: Array
        +nodes: NodeSchema[]
        +dataSources: object
    }

    class BlockModel {
        +update(schema silent)
        +toDsl(version)
        +addNode(node target position)
        +removeNode(node silent)
        +setState(name value silent)
        +setFunction(type name value silent)
        +setCss(content silent)
        +dispose()
    }

    BlockModel ..> BlockSchema : 实例化

该模式支持 Vue 的 Composition API 模式,包括响应式状态、计算属性、侦听器、生命周期钩子以及通过 inject 属性进行依赖注入。

页面模板

页面模板将块的概念扩展到完整的页面布局,通过扩展 BlockFilePageFile 接口支持路由、导航和页面级配置。

页面文件扩展

页面继承所有块属性,同时添加页面特定配置:

interface PageFile extends BlockFile {
  dir?: boolean; // 目录与页面
  layout?: boolean; // 布局页面标志
  icon?: string; // 菜单图标
  children?: PageFile[]; // 目录的子页面
  mask?: boolean; // 在主模板内
  hidden?: boolean; // 从菜单隐藏
  raw?: boolean; // 源代码页面(非低代码)
  pure?: boolean; // 纯页面标志
  cache?: boolean; // 启用页面缓存
  meta?: Record<string, any>; // 路由元数据
  needLogin?: boolean; // UniApp 登录要求
  style?: Record<string, any>; // UniApp 窗口配置
}

这些属性支持页面级配置,包括路由、导航菜单生成、缓存策略和平台特定行为。

模板市场集成

VTJ 提供了一个远程模板市场系统,支持跨项目发现、安装和管理预构建的模板。

模板数据模型

来自市场的模板遵循 TemplateDto 结构:

interface TemplateDto {
  id: string; // 模板 ID
  name: string; // 模板名称
  label: string; // 显示标签
  category: string; // 类别分组
  author: string; // 作者标识符
  userId: string; // 用户 ID
  cover: string; // 封面图片 URL
  vip?: boolean; // VIP 标志
  share?: boolean; // 共享标志
}

模板按作者所有权(用户的模板与共享模板)和类别组织,支持个性化浏览和发现。

模板安装流程

安装模板涉及从远程市场获取 DSL 并将其与当前页面或块配置合并。

graph TD
    A[用户点击使用] --> B{是否已登录?}
    B -- 否 --> C[显示登录对话框]
    C --> N{用户登录?}
    N -- 是 --> D
    N -- 否 --> O[取消]
    B -- 是 --> D[获取模板 DSL]
    D --> E{DSL 可用?}
    E -- 否 --> F[错误: 模板未发布]
    E -- 是 --> G[获取当前文件 DSL]
    G --> H[保留 ID 和名称]
    H --> I[合并模板 DSL]
    I --> J[更新项目中的文件]
    J --> K[触发保存事件]
    K --> L[更新当前模型]
    L --> M[成功消息]

安装过程保留当前文件的身份(ID 和名称),同时用模板 DSL 替换内容,确保现有文件引用保持完整。

设计器中的块管理

设计器提供了一个全面的块小部件,支持块的创建、编辑、搜索和拖放操作。

块小部件功能

块小部件通过有组织的 UI 支持几个关键操作:

graph TD
    subgraph "Block Widget"
        S[Search Input]
        C[Category Collapse]
        I[Block Items]
    end

    subgraph "Block Operations"
        A[Add Block]
        E[Edit Block]
        CP[Copy Block]
        R[Remove Block]
        DRG[Drag to Canvas]
    end

    S --> I
    C --> I
    I --> A
    I --> E
    I --> CP
    I --> R
    I --> DRG

块按类别分组,带有可折叠的部分,显示计数徽章,并支持对名称和标题进行关键词搜索。

块创建配置

创建新块需要通过对话框表单配置基本属性:

属性必填描述验证
fromType块来源类型Schema/UrlSchema/Plugin(创建后禁用)
name块标识符英文驼峰式命名,正则模式验证
title显示标题用户友好的文本
library条件插件库名称仅 Plugin 类型需要
urls条件资源 URLPlugin/UrlSchema 类型需要,文件上传器
category分组类别带有过滤/创建选项的选择

名称字段使用正则验证(NAME_REGEX)以确保代码生成使用正确的标识符格式。

提示: 块名称必须遵循驼峰式命名约定,并根据现有块名称进行验证以防止重复。createEmtpyModel 函数默认创建一个带有 fromType: 'Schema' 的新模板,为新块奠定基础。

设计器中的模板管理

模板小部件提供市场访问,具有分类、过滤和安装功能。

模板组织

模板通过选项卡式界面组织,将用户拥有的模板与共享模板分开:

graph TD
    T[All Templates] --> G1[My Templates]
    T --> G2[Shared Templates]
    G1 --> C1[Category 1]
    G1 --> C2[Category 2]
    G2 --> C3[Category N]
    C1 --> TI1[Template 1]
    C2 --> TI2[Template 2]
    C3 --> TIN[Template N]

isOwner 函数根据用户 ID 和共享状态确定编辑/删除权限,确保用户只能管理自己的非共享模板。

模板安装要求

模板安装需要身份验证并遵循权限模型:

条件操作用户体验
未登录显示登录确认带有登录按钮的“模板需要登录”对话框
已登录,模板可用安装模板“模板已加载”成功消息
已登录,模板不可用错误通知“模板未发布版本”错误警报
模板安装到页面更新页面 DSL当前页面内容被模板替换

安装过程使用 engine.onSaveBlockFileFinish 确保文件修改后安全更新状态。

核心实现模式

理解底层的实现模式可以有效地扩展和定制模板系统。

块模型生命周期

BlockModel 通过事件驱动更新管理块状态:

stateDiagram
    [*] --> Created : constructor(schema)
    Created --> Updated : update(schema, silent=false)
    Updated --> Updated : emit EVENT_BLOCK_CHANGE
    Created --> Disposed : dispose()
    Updated --> Disposed : nodes cleared

更新方法中的 silent 参数控制是否发出更改事件,允许批量修改而不触发响应式级联。

状态管理方法

BlockModel 为块功能的不同方面提供细粒度的状态操作方法:

// 函数管理 (methods, computed, lifeCycles)
setFunction(type, name, value, silent);
removeFunction(type, name, silent);

// 状态管理
setState(name, value, silent);
removeState(name, silent);

// 侦听器管理
setWatch(watch, silent);
removeWatch(watch, silent);

// Props, Emits, Slots
setProp(prop, silent);
removeProp(prop, silent);
setEmit(emit, silent);
removeEmit(emit, silent);
setSlot(slot, silent);
removeSlot(slot, silent);

除非传递 silent: true,否则每个方法都会触发 EVENT_BLOCK_CHANGE,从而实现对响应式的细粒度控制。

项目模型集成

ProjectModel 作为块和页面的中央注册表,提供 CRUD 操作和验证:

// 块操作
createBlock(block: BlockFile, silent: boolean)
updateBlock(block: BlockFile, silent: boolean)
cloneBlock(block: BlockFile, silent: boolean)
removeBlock(id: string, silent: boolean)
existBlockName(name: string, excludes: string[]): boolean

// 页面操作 (扩展块)
createPage(page: PageFile, parentId?: string, silent: boolean)
updatePage(page: PageFile, silent: boolean)
clonePage(page: PageFile, parentId?: string, silent: boolean)
removePage(id: string, silent: boolean)
existPageName(name: string, excludes: string[]): boolean

isPageFile 类型守卫区分页面文件和块文件,实现类型安全操作。

提示: ProjectModel 在 project.blocks 数组中维护块引用,并通过 existBlockNameexistPageName 方法提供验证,防止项目结构中的命名冲突。

高级模板功能

VTJ 的模板系统支持复杂组件架构的高级功能。

依赖注入

块可以通过 inject 数组声明依赖,启用组合模式:

interface BlockInject {
  name: string; // 注入键
  from?: string | JSExpression; // 源表达式
  default?: JSONValue | JSExpression; // 默认值
}

这使块能够从父上下文接收服务、工具或配置。

插槽定义

块可以定义带有类型参数的命名插槽以进行内容投影:

interface BlockSlot {
  name: string; // 插槽标识符
  params: string[]; // 作用域插槽的参数名称
}

插槽支持灵活的内容组合,同时通过参数声明保持类型安全。

事件发出

块通过 emits 数组声明自定义事件以与父组件通信:

interface BlockEmit {
  name: string; // 事件名称
  params: string[]; // 参数类型
}

事件定义支持具有 TypeScript 感知的事件处理和文档生成。

集成点

模板系统与多个 VTJ 子系统集成,以提供全面的功能。

设计器集成

设计器提供用于块和模板管理的小部件面板:

  • Blocks Widget:支持 CRUD 操作的本地块管理
  • Templates Widget:市场模板浏览和安装
  • Drag & Drop:块可以直接拖放到设计画布上
  • Context Menu:右键单击操作,用于编辑、复制和移除

渲染器集成

块通过 VTJ 渲染器在运行时渲染,支持:

  • Property Binding:通过表达式进行动态属性绑定
  • Event Handling:事件发出和父组件通信
  • Slot Composition:命名和作用域插槽渲染
  • State Management:带有侦听器和计算属性的响应式状态

渲染器解释 BlockSchema 以生成具有完整 Composition API 支持的 Vue 组件。

代码生成集成

块通过代码生成器生成 Vue SFC 代码,产生:

  • Component Definition:Props、emits、expose 声明
  • Setup Function:状态、方法、计算属性、侦听器
  • Template:转换为 Vue 模板语法的节点
  • Style:来自块模式的 CSS 内容
  • Script:生命周期钩子和依赖注入

最佳实践

块设计指南

  1. 单一职责:设计块以实现专注的、可复用的功能
  2. 类型安全:使用显式类型声明 props 和 emits
  3. 文档:使用有意义的标题和描述
  4. 分类:使用 category 属性对相关块进行分组
  5. 版本控制:使用 __VERSION__ 进行模板版本跟踪

模板组织

  1. 类别结构:使用分层类别以提高可发现性
  2. 封面图片:为模板提供清晰的预览图片
  3. 文档:在模板描述中包含使用说明
  4. 版本管理:在不同模板版本之间保持向后兼容性

性能考虑

  1. 懒加载:对大型外部块库使用 UrlSchema
  2. 代码分割:将复杂的块拆分为更小的可复用单元
  3. 资源优化:最小化 Plugin 块中的 CSS 和 JS 资源

下一步

要加深您对 VTJ 模板系统及相关概念的理解:

  • 探索 Material Schema Configuration 以了解详细的组件属性定义
  • 了解 Creating Custom Material Components 以构建可复用组件
  • 查看 DSL to Vue Code Generation 以了解块如何转换为运行时代码
  • 研究 Engine API Reference 以进行编程块操作
  • 检查 Project Structure and File Organization 以了解布局策略

参考资料