简介
本设计文档面向 DouPHP 框架的视图层,聚焦模板引擎架构与 DWT 模板语法、变量渲染、条件判断、循环结构、模板继承与包含机制;说明 CSS/JS 资源加载与管理方式、响应式实现;阐述前后端分离场景下的视图处理方式与 SEO 优化要点。文档以代码级分析为基础,配合图示帮助读者快速理解并高效开发。
项目结构
- 模板引擎位于 core/web/template,提供编译式模板引擎:词法分析、解析、代码生成与运行期上下文管理。
- 主题模板位于 theme/<theme>/,采用 .dwt 扩展名,通过控制器渲染到浏览器。
- 控制器位于 front/controller/*,负责组装数据并通过 view() 返回模板渲染结果。
- 路由与 URL 规则在 config/route.php 中配置,支持多种风格化静态后缀(如 .html)。
graph TB
subgraph "前端请求"
C["浏览器"]
end
subgraph "路由与控制器"
R["路由配置<br/>config/route.php"]
IC["首页控制器<br/>front/controller/index/IndexController.php"]
PC["页面控制器<br/>front/controller/page/PageController.php"]
end
subgraph "模板引擎"
DV["DouView<br/>core/web/template/DouView.php"]
DVC["编译器<br/>core/web/template/DouViewCompiler.php"]
LEX["词法分析器<br/>core/web/template/Lexer.php"]
end
subgraph "主题模板"
T1["首页模板<br/>theme/default/index.dwt"]
INC["局部片段<br/>inc/*.tpl"]
end
C --> R --> IC
IC --> DV
DV --> DVC --> LEX
DVC --> |"编译产物"| DV
DV --> |"include/标签"| T1
T1 --> INC
图表来源
- DouView.php:158-216
- DouViewCompiler.php:90-104
- Lexer.php:44-141
- index.dwt:1-50
- IndexController.php:58-78
- route.php:271-299
章节来源
- DouView.php:158-216
- DouViewCompiler.php:90-104
- Lexer.php:44-141
- index.dwt:1-50
- IndexController.php:58-78
- route.php:271-299
核心组件
- 模板引擎主类 DouView:负责变量作用域、预处理器、编译缓存、模板路径解析与渲染输出。
- 编译器 DouViewCompiler:串联 Lexer → Parser → CodeGenerator,完成模板源到 PHP 的转换。
- 词法分析器 Lexer:单趟状态机扫描,识别注释、literal、php 块与普通标签,转义内联 PHP 防止注入。
- 标签编译器注册表:集中注册 include、if、foreach、list、category、url、assign、strip、comment、delim、break/continue 等标签。
- 门户循环基类 AbstractPortalLoopTagCompiler:统一 list/category 的数据获取与循环语义,强制 module 为字面量,保障安全。
- CategoryTagCompiler:实现 {category} 标签,支持分类树与“每类内容”模式。
章节来源
- DouView.php:24-77
- DouViewCompiler.php:39-104
- Lexer.php:21-33
- DouViewCompiler.php:172-204
- AbstractPortalLoopTagCompiler.php:21-50
- CategoryTagCompiler.php:23-33
架构总览
视图层遵循“控制器准备数据 → 模板引擎编译渲染 → 主题模板组合片段”的分层模型。控制器通过 view() 将数据传入模板引擎;引擎按版本与修订号进行编译缓存;运行时通过 RenderContext 暴露 $ctx 访问变量与过滤器;模板使用 DWT 语法组织布局与数据展示。
sequenceDiagram
participant U as "用户"
participant RT as "路由"
participant CT as "控制器"
participant V as "DouView"
participant CV as "编译器"
participant TX as "模板文件"
U->>RT : 请求URL
RT->>CT : 匹配路由并调用方法
CT->>V : view("xxx.dwt", 数据)
V->>CV : compile(模板源)
CV-->>V : 编译后的PHP
V->>TX : include/标签展开
TX-->>U : HTML响应
图表来源
- IndexController.php:58-78
- DouView.php:158-216
- DouViewCompiler.php:90-104
- index.dwt:19-42
详细组件分析
模板引擎编译管线
- 预处理:Prefilter 可在编译前对模板源做变换(例如注入全局变量或改写语法)。
- 词法切分:Lexer 将模板源切分为 TokenStream,识别注释、literal、php 块与普通标签,并对文本块中的疑似 PHP 起止符进行转义,避免注入。
- 解析与代码生成:Parser 构建 AST,CodeGenerator 生成可执行的 PHP 代码;内置标签由 TagCompilerRegistry 统一管理。
- 缓存策略:基于 COMPILE_REVISION 与 VERSION 的编译缓存,确保升级后自动失效重编。
flowchart TD
A["模板源"] --> B["Prefilter 预处理"]
B --> C["Lexer 词法切分"]
C --> D["Parser 解析AST"]
D --> E["CodeGenerator 生成PHP"]
E --> F["写入编译缓存"]
F --> G["运行时 include 执行"]
图表来源
- DouView.php:132-156
- DouView.php:198-216
- DouViewCompiler.php:90-104
- Lexer.php:44-141
章节来源
- DouView.php:132-156
- DouView.php:198-216
- DouViewCompiler.php:90-104
- Lexer.php:44-141
DWT 模板语法与最佳实践
- 变量渲染:使用 {$var} 形式输出变量,控制器通过 view() 传入键值对。
- 条件判断:使用 <!-- {if ...} --> 与 <!-- {/if} --> 包裹条件区域。
- 循环结构:
- {foreach}:通用数组遍历,支持 @first/@last 等循环属性。
- {list}/{category}:门户数据块,统一通过 Portal 取数,支持 item/key/name/offset 与空态分支。
- 模板包含:{include file="..."} 引入公共片段,便于复用 header/footer 等布局。
- 安全建议:模块名必须为字面量(编译期校验),避免将请求参数注入模块名;开启全局 HTML 转义或在输出时显式过滤。
classDiagram
class AbstractPortalLoopTagCompiler {
+tagName() string
+portalMethod() string
}
class CategoryTagCompiler {
+tagName() string
}
class ForeachTagCompiler
class IncludeTagCompiler
class IfTagCompiler
AbstractPortalLoopTagCompiler <|-- CategoryTagCompiler
IncludeTagCompiler --> AbstractPortalLoopTagCompiler : "组合使用"
IfTagCompiler --> AbstractPortalLoopTagCompiler : "条件控制"
ForeachTagCompiler --> AbstractPortalLoopTagCompiler : "循环语义对齐"
图表来源
- AbstractPortalLoopTagCompiler.php:21-50
- CategoryTagCompiler.php:23-33
- DouViewCompiler.php:172-204
章节来源
- index.dwt:19-42
- AbstractPortalLoopTagCompiler.php:21-50
- CategoryTagCompiler.php:23-33
- DouViewCompiler.php:172-204
模板继承与包含机制
- 包含机制:通过 {include file="..."} 引入 inc/*.tpl 等片段,实现 header/footer/slide_show 等复用。
- 继承模式:当前模板系统以“包含+片段”为主;如需更严格的继承,可通过控制器选择不同模板文件或在 include 中组合布局。
- 命名规范:模板文件使用 .dwt 扩展名;片段使用 .tpl 扩展名;主题目录 theme/<theme>/ 下组织 css/js/images/inc 等资源。
章节来源
- index.dwt:19-42
CSS/JS 资源加载与管理
- 资源位置:theme/<theme>/css、js、images 目录存放样式、脚本与图片。
- 加载方式:在模板 head/body 中直接 link/script 引用;也可通过控制器注入动态资源(例如 code_head)。
- 版本与缓存:建议对静态资源添加版本号或使用 CDN;结合服务器缓存头提升性能。
- 模块化:将通用样式与业务样式拆分,按需加载减少首屏体积。
章节来源
- index.dwt:13-18
- index.dwt:43-47
响应式设计实现
- 视口设置:模板 head 中声明 viewport meta 以启用移动端缩放。
- 媒体查询:在各主题 CSS 中使用 @media 定义断点,适配不同屏幕尺寸。
- JS 辅助:部分主题使用 respond.js/css3-mediaqueries.js 兼容旧浏览器媒体查询能力。
- 最佳实践:优先使用流式布局与相对单位;图片 max-width:100%;避免固定宽度容器。
章节来源
- index.dwt:5-6
- _'\theme\m121\js\respond.js:70-237
- _'\theme\m118\js\css3-mediaqueries.js:630-778
前后端分离下的视图处理
- 传统服务端渲染:控制器准备数据,模板引擎渲染 HTML,适合 SEO 友好型站点。
- 渐进式分离:保留服务端模板渲染首屏,后续交互通过 API 异步更新;模板中通过 URL 标签与接口对接。
- 数据契约:控制器仅暴露必要字段给模板;复杂逻辑下沉至服务层,保持模板简洁。
章节来源
- IndexController.php:58-78
- PageController.php:133-138
SEO 优化要点
- 元信息:通过 SeoResolver 统一生成 title/keywords/description,并在模板 head 中输出。
- 结构化数据:可在 code_head 中注入 JSON-LD 等结构化标记。
- URL 规则:通过 route.php 配置静态后缀(如 .html)与归档风格,利于搜索引擎抓取。
- 面包屑与导航:控制器注入 breadcrumb/nav 数据,增强站点结构与可访问性。
章节来源
- SeoResolver.php:26-39
- route.php:271-299
- BrandController.php:141-151
- SupportController.php:237-244
依赖关系分析
- 控制器依赖服务层(IndexService、NavigationBuilder、SeoResolver)准备数据。
- 模板引擎依赖编译器、词法分析器、标签编译器注册表。
- 主题模板依赖 inc 片段与静态资源。
- 路由配置影响 URL 风格与模板选择。
graph LR
IC["IndexController"] --> IS["IndexService"]
IC --> NB["NavigationBuilder"]
IC --> SR["SeoResolver"]
IC --> DV["DouView"]
DV --> DVC["DouViewCompiler"]
DVC --> LEX["Lexer"]
DVC --> REG["TagCompilerRegistry"]
DV --> TM["theme/default/index.dwt"]
TM --> INC["inc/*.tpl"]
图表来源
- IndexController.php:58-78
- DouViewCompiler.php:172-204
- index.dwt:19-42
章节来源
- IndexController.php:58-78
- DouViewCompiler.php:172-204
- index.dwt:19-42
性能考虑
- 编译缓存:利用 COMPILE_REVISION 与 VERSION 自动失效机制,避免重复编译。
- 模板路径校验:resolveTemplatePath 防止路径穿越与越界访问,提升安全性与稳定性。
- 资源合并与压缩:生产环境合并 CSS/JS 并启用 gzip;图片使用 WebP/懒加载。
- 数据分页与增量:列表页使用 offset/limit,避免一次性加载大量数据。
- 条件渲染:合理使用 if/foreach,减少不必要的 DOM 与脚本执行。
故障排查指南
- 模板未找到:检查 template_dir 与模板文件名大小写、扩展名白名单(tpl/htm/html/dwt)。
- 编译错误:查看编译缓存目录,确认 COMPILE_REVISION 是否变化导致重编;检查标签语法与闭合。
- 变量未显示:确认控制器已 assign 对应键;检查 escapeHtml 是否转义了预期内容。
- 注入风险:确保 module 为字面量;避免在模板中拼接不可信输入;必要时开启全局 HTML 转义。
- 资源 404:核对 theme 目录结构与资源路径;确认服务器重写规则正确。
章节来源
- DouView.php:238-274
- DouView.php:39-55
- AbstractPortalLoopTagCompiler.php:21-50
结论
DouPHP 视图层以编译式模板引擎为核心,提供安全的词法分析、灵活的标签体系与高效的缓存机制。通过控制器与服务层解耦,模板专注于展示与组合;配合 SEO 工具与路由规则,可实现高性能、易维护的前端体验。建议在大型项目中坚持“数据在服务层、展示在模板层”的原则,持续优化资源加载与渲染性能。
附录
- 常用标签速查:
- 变量:{$var}
- 条件:<!-- {if ...} --> ... <!-- {/if} -->
- 循环:{foreach}, {list}, {category}
- 包含:{include file="..."}
- URL:{url ...}
- 注释:{ ... }
- 字面量:{literal}...{/literal}
- PHP 块:{php}...{/php}
- 模板文件命名:.dwt;片段:.tpl;资源目录:css/js/images/inc。
- 控制器示例:首页、页面、品牌、支持等控制器展示了数据注入与 layoutVars 的使用。
章节来源
- index.dwt:1-50
- IndexController.php:58-78
- PageController.php:133-138
- BrandController.php:130-151
- SupportController.php:203-244