文档目录
组件化开发

简介

本指南面向需要在 DouPHP 中构建模块化界面的开发者,系统阐述“组件化”的设计思想与在 DouPHP 中的落地方式。文档围绕以下目标展开:

  • 解释组件化的设计理念与在 DouPHP 中的实现路径
  • 给出自定义组件的创建步骤(文件结构、数据绑定、事件处理)
  • 说明内置组件的使用方法与扩展机制
  • 提供可复用的 UI 组件与业务组件示例
  • 介绍组件间通信模式与状态管理策略
  • 总结性能优化技巧与调试方法

项目结构

DouPHP 的组件化体现在三层协同:

  • 模板引擎层:编译式模板引擎负责将模板资源解析为可执行代码,并提供变量作用域、过滤器、预处理器等能力,支撑“片段/区块”级别的复用。
  • 业务服务层:以 Fragment 为代表的“内容碎片”服务,提供树形组装、附件处理、关联盒子等能力,是“内容型组件”的数据与行为中心。
  • 前台渲染层:通过模型与语言包将碎片数据按唯一标记组织,供模板按需引用,形成“可插拔的内容块”。
graph TB
A["控制器/路由"] --> B["模板引擎<br/>DouView"]
B --> C["编译产物缓存"]
B --> D["模板资源<br/>.dwt/.htm/.tpl"]
A --> E["业务服务<br/>FragmentService"]
E --> F["ORM/数据库"]
E --> G["附件服务"]
A --> H["前台模型<br/>Fragment"]
H --> I["语言包/国际化"]
D --> J["主题/模板目录"]

核心组件

  • 模板引擎组件(DouView)
    • 职责:模板解析、编译、缓存、上下文变量注入、全局 HTML 转义开关、前置过滤器扩展点。
    • 关键点:fetch/display/renderResource/compileSource;编译修订号控制缓存失效;模板路径白名单与安全校验。
  • 内容碎片组件(Fragment 系列)
    • 后台服务:FragmentService 负责碎片树构建、新增/更新/删除、图片附件存储、与“内容盒子”的关联切换。
    • 前台模型:Fragment::fragment() 按 mark 索引返回成品数组,支持多语言与文本行拆分,便于模板直接消费。
  • 模板视图组件(fragment.htm)
    • 职责:后台碎片的列表展示、表单编辑、与内容盒子的关联操作入口。

架构总览

下图展示了从请求到页面渲染的完整链路,以及“碎片组件”在其中的角色。

sequenceDiagram
participant U as "用户"
participant R as "路由/控制器"
participant S as "FragmentService"
participant M as "Fragment(前台模型)"
participant T as "DouView(模板引擎)"
participant V as "模板(.dwt/.htm)"
U->>R : 访问页面
R->>M : 获取碎片数据 fragment()
M-->>R : 按 mark 索引的碎片数组
R->>T : assign(变量) + fetch("模板名")
T->>T : 解析/编译/缓存命中判断
T->>V : 渲染模板并输出HTML
V-->>U : 最终页面

详细组件分析

模板引擎组件(DouView)

  • 设计要点
    • 编译管线:Prefilter → Lexer → Parser → CodeGenerator,运行时通过 RenderContext 暴露变量与作用域。
    • 安全与隔离:模板路径白名单、路径穿越防护、可选的全局 HTML 转义。
    • 缓存策略:基于源文件与编译产物时间戳、版本头(COMPILE_REVISION/VERSION)判定是否重编。
  • 关键流程
    • fetch:准备上下文、开启缓冲、调用 renderResource 并返回结果。
    • renderResource:定位模板、检查是否需要重编译、写入编译产物、include 执行。
    • compileSource:执行预处理器后交由编译器生成 PHP。
  • 扩展点
    • registerPrefilter:在编译前对源做变换,适合做模板级增强或动态注入。
    • setEscapeHtml:统一控制输出转义,提升安全性。
flowchart TD
Start(["进入 fetch"]) --> Ctx["准备上下文与循环状态"]
Ctx --> Render["renderResource 解析模板"]
Render --> NeedRecomp{"需要重编译?"}
NeedRecomp -- 否 --> Include["include 编译产物"]
NeedRecomp -- 是 --> Compile["compileSource 编译"]
Compile --> WriteCache["写入编译缓存"]
WriteCache --> Include
Include --> End(["返回渲染结果"])

内容碎片组件(Fragment 系列)

  • 后台服务(FragmentService)
    • 树形构建:根据 parent_id 递归组装 child,同时拼装 content(图片/文本)与 box_list(关联盒子)。
    • 增删改:insert/update/delete 结合附件服务完成图片上传/重命名/删除,审计日志记录。
    • 关联切换:toggleLinkBox 在碎片与“内容盒子”之间切换关联。
  • 前台模型(Fragment)
    • 接口:静态方法 fragment() 返回按 mark 索引的成品数组,关闭特性时返回空数组。
    • 数据处理:多语言适配、文本按行拆分、附件 URL 转换。
  • 后台视图(fragment.htm)
    • 列表与编辑:展示碎片树、编辑表单字段(名称、标记、图片、文本、链接、首页显示、父级分组),提供与内容盒子关联的操作入口。
classDiagram
class FragmentService {
+buildFragmentList(parent_id, current_id, home) array
+insert(data) int
+update(data) void
+delete(id, data) array
+toggleLinkBox(id) void
}
class Fragment_前台模型 {
+fragment() array
}
class DouView {
+assign(var, value) void
+fetch(template) string
+display(template) void
+compileSource(name, source) string
}
FragmentService --> Fragment_前台模型 : "读取/写入数据"
DouView --> Fragment_前台模型 : "模板侧消费数据"

自定义组件创建步骤

  • 文件结构建议
    • 模板资源:放在主题或公共模板目录,使用 .dwt/.htm/.tpl 后缀,遵循模板引擎白名单。
    • 数据模型:若涉及持久化,参考 Fragment 模型,定义表映射、类型转换、预取器。
    • 业务服务:封装复杂逻辑(如树形组装、附件处理、关联关系),对外暴露清晰方法。
    • 视图模板:提供列表、表单、详情等视图,使用模板标签进行条件渲染与循环。
  • 数据绑定
    • 控制器/门面通过模板引擎的 assign 将数据注入上下文,模板中以 $ctx-> 或模板变量形式消费。
    • 对于“碎片类”组件,推荐按 mark 索引组织数据,便于模板按需引用。
  • 事件处理
    • 前端交互通过模板内联脚本或外部 JS 触发 AJAX,调用后端服务接口,再由服务更新数据并返回结果。
    • 可在服务层抛出领域异常并重定向,配合模板提示用户。
  • 示例:创建一个“卡片组件”
    • 模板:card.dwt,包含标题、图片、描述、链接。
    • 模型:Card 模型,定义字段与附件映射。
    • 服务:CardService,提供列表、详情、创建、更新、删除等方法。
    • 视图:后台管理页使用 fragment.htm 的模式组织卡片列表与编辑表单。
    • 集成:在页面模板中通过模板引擎引入 card.dwt,并传入数据。

内置组件使用方法与扩展机制

  • 内置组件
    • 内容碎片:通过前台模型 Fragment::fragment() 获取并按 mark 引用,适合首页内容、公告、轮播等场景。
    • 模板引擎:通过 DouView 提供的 assign/fetch/display 进行变量注入与模板渲染,支持预处理器扩展。
  • 扩展机制
    • 模板预处理器:registerPrefilter 在编译前对模板源做变换,可用于注入通用片段或动态语法。
    • 过滤器注册:StandardFilters 已注册常用过滤器,可按需扩展更多过滤逻辑。
    • 模块开关:通过 module.php 配置启用/禁用模块,控制菜单与导航显示。

组件间通信模式与状态管理策略

  • 通信模式
    • 模板到服务:通过表单提交或 AJAX 调用后端服务,服务更新数据后返回结果。
    • 服务到模板:服务将数据组织为模板友好的结构(如按 mark 索引),由模板渲染。
    • 跨组件共享:通过模板上下文变量或全局配置(如 features.*)控制组件行为。
  • 状态管理
    • 前端状态:建议在模板中使用局部变量与循环状态,避免全局污染。
    • 后端状态:在服务层维护事务性操作,确保数据一致性;必要时使用缓存或队列异步处理。
    • 特性开关:通过配置项(如 features.fragment)控制组件是否启用,便于灰度发布与回滚。

依赖关系分析

  • 组件耦合
    • 模板引擎与模板资源:强耦合,但通过白名单与路径校验保障安全。
    • 服务与模型:服务依赖模型进行数据读写,模型负责数据映射与预处理。
    • 视图与服务:视图仅负责展示,逻辑下沉至服务,降低耦合。
  • 外部依赖
    • 附件服务:用于图片上传、重命名、删除。
    • 语言包:支持多语言内容。
    • 配置系统:控制模块开关与功能特性。
graph LR
Tpl["模板引擎<br/>DouView"] --> |渲染| View["模板资源<br/>.dwt/.htm/.tpl"]
Svc["业务服务<br/>FragmentService"] --> |读写| Model["数据模型<br/>Fragment"]
Svc --> |调用| Attach["附件服务"]
Model --> |使用| Lang["语言包"]
Svc --> |读取| Config["配置系统"]

性能考虑

  • 模板编译缓存:利用 DouView 的编译缓存机制,减少重复编译开销;合理设置 COMPILE_REVISION 控制缓存失效。
  • 数据查询优化:在服务层使用合适的查询条件与排序,避免全表扫描;对大对象使用分页或懒加载。
  • 附件处理:批量上传时注意磁盘 IO 与网络延迟,必要时使用异步任务。
  • 模板渲染:减少模板中的复杂逻辑,将计算下沉至服务层;合理使用过滤器与预处理器。
  • 特性开关:通过配置项关闭不需要的组件,减少不必要的查询与渲染。

故障排查指南

  • 常见问题
    • 模板未找到:检查模板路径是否在白名单内,且位于 template_dir 下。
    • 编译失败:查看编译错误日志,确认模板语法正确;必要时临时关闭缓存以定位问题。
    • 数据为空:检查特性开关(如 features.fragment)是否启用;确认数据库记录是否存在。
    • 附件异常:检查附件服务配置与权限;确认上传目录可写。
  • 调试方法
    • 启用调试模式:在开发环境开启详细错误信息。
    • 打印上下文:在服务层打印关键变量,确认数据流转。
    • 逐步验证:从模板到服务再到模型,逐层验证数据与逻辑。

结论

DouPHP 的组件化以模板引擎为核心,结合服务层与模型层,实现了“内容碎片”的可插拔与复用。通过清晰的职责划分、安全的模板解析、灵活的服务扩展,开发者可以高效构建模块化界面。建议在实际项目中遵循本文档的结构与最佳实践,持续提升代码质量与可维护性。

附录

  • 快速开始
    • 启用碎片特性:在配置中开启 features.fragment。
    • 创建碎片:通过后台视图添加碎片,设置名称、标记、图片、文本、链接。
    • 模板引用:在前台模板中通过 Fragment::fragment() 获取并按 mark 引用。
  • 参考路径
    • 模板引擎:core/web/template/DouView.php
    • 碎片服务:admin/service/fragment/FragmentService.php
    • 碎片模型:front/model/fragment/Fragment.php
    • 后台视图:admin/view/fragment.htm
    • 模块配置:config/module.php
添加日期:2026-10-05