简介
本文面向 DouPHP 小程序“文章管理页面”的开发与维护,覆盖文章列表页的分类展示、搜索过滤、分页加载,以及文章详情页的富文本渲染、图片处理、分享功能。文档同时给出数据结构设计、缓存与性能优化方案、SEO 实现要点、移动端兼容性建议与常见问题解决方案,帮助开发者快速落地并持续优化。
项目结构
围绕文章能力的前后端关键位置如下:
- 前端控制器与服务:负责路由解析、分页、分类筛选、归档、详情构建、点击统计、SEO 注入等。
- 模型层:文章与分类的数据访问、关联查询、格式化(附件 URL、时间、自定义属性)与多语言支持。
- 小程序端:文章详情页通过 mp-html 组件渲染富文本内容,使用 http 服务获取数据,配置分享标题。
- SEO:结构化数据与页面元信息生成。
graph TB
subgraph "小程序端"
MP_TS["article.ts"]
MP_WXML["article.wxml"]
MP_HTML["mp-html 组件"]
end
subgraph "Web 前台"
CTRL["ArticleController"]
SVC["ArticleService"]
MODEL_A["Article 模型"]
MODEL_C["ArticleCategory 模型"]
SEO["SchemaService"]
end
MP_TS --> |"HTTP 请求"| CTRL
MP_WXML --> MP_HTML
CTRL --> SVC
SVC --> MODEL_A
SVC --> MODEL_C
CTRL --> SEO
核心组件
- 文章控制器(ArticleController)
- 列表/分类/归档:解析路由参数、分页、构建分类信息与面包屑、SEO 元信息、返回视图数据。
- 详情:校验 ID、构建详情数据、记录点击量、组装评论(可选)、输出 Schema 与视图数据。
- 文章服务(ArticleService)
- 列表数据:按分类、归档、默认排序进行分页,构造精简列表项(含 URL、分类信息、摘要截断)。
- 详情数据:查找已发布文章,将内容经 Markdown 渲染器转换为 HTML。
- 分类查询与点击统计:提供分类单行与点击量自增。
- 文章模型(Article / ArticleCategory)
- 列表读取走 AR:with('category') 批量取分类;casts 格式化 image/defined/created_at;prefetchers 预热 url/多语言/附件;translatable 覆写 title/content/description/keywords 当前语言值。
- 列表 Presenter 字段(url/name/description/cate_info/add_time_short/time)由 concerns trait 经 accessor + $appends 承接。
- 小程序文章详情页
- 通过 http 调用 article.show 接口,设置页面标题与分享标题,使用 mp-html 渲染富文本内容。
- SEO 结构化数据
- 为文章页生成 Article 类型 JSON-LD,包含标题、封面、发布时间、作者、发布者、主页面等信息。
架构总览
下图展示了从小程序到 Web 前台再到模型与 SEO 的完整调用链。
sequenceDiagram
participant MP as "小程序 article.ts"
participant CTRL as "ArticleController"
participant SVC as "ArticleService"
participant MOD as "Article/ArticleCategory"
participant SEO as "SchemaService"
MP->>CTRL : GET /article/show?id=...
CTRL->>SVC : buildArticleShowData(id)
SVC->>MOD : findPublishedById(id)
MOD-->>SVC : 文章实体
SVC->>SVC : 内容转HTML(MarkdownRenderer)
SVC-->>CTRL : 文章数据
CTRL->>SVC : recordArticleView(id)
CTRL->>SEO : article(article, cateInfo)
SEO-->>CTRL : JSON-LD
CTRL-->>MP : {article, defined}
MP->>MP : 设置分享标题
详细组件分析
文章列表页(分类/归档/分页)
- 分类与归档
- 通过路由参数解析分类 ID 与日期归档(年/月),归档时强制全部分类语境。
- 构建分类树与面包屑,用于导航与 SEO。
- 分页
- 每页条数来自配置,默认 10;分页 URL 根据是否归档动态生成。
- 列表数据
- 批量预加载分类,格式化附件、时间、自定义属性;摘要截断;附加分类 URL 与名称。
- 搜索过滤
- 侧栏搜索框回填全站搜索关键词 q(需通过安全校验),实际检索逻辑由全局搜索模块承担;列表页仅做回填与上下文传递。
flowchart TD
Start(["进入列表页"]) --> Parse["解析路由参数<br/>分类ID/年/月/页码"]
Parse --> Archive{"是否归档?"}
Archive --> |是| SetCat["设置分类为全部分类"]
Archive --> |否| KeepCat["保持原分类"]
SetCat --> Query["查询文章列表<br/>with(category)+分页"]
KeepCat --> Query
Query --> BuildList["构造列表项<br/>URL/摘要/分类信息"]
BuildList --> Render["渲染视图/分页/面包屑/SEO"]
Render --> End(["完成"])
文章详情页(富文本/图片/分享)
- 数据获取
- 控制器校验 ID,服务层查找已发布文章并渲染内容为 HTML。
- 记录点击量,生成 SEO 结构化数据。
- 富文本渲染
- 小程序端使用 mp-html 组件渲染 article.content 的 HTML。
- 若需要自定义样式或安全过滤,可在 mp-html 组件中扩展。
- 图片处理
- 图片资源以附件形式存储,模型 casts 会将其转为可用 URL;建议在 mp-html 中启用懒加载与尺寸适配。
- 分享
- 页面 onLoad 时设置分享标题为文章标题;onShareAppMessage 返回标题。
sequenceDiagram
participant MP as "小程序 article.ts"
participant WXML as "article.wxml"
participant HTML as "mp-html"
participant CTRL as "ArticleController"
participant SVC as "ArticleService"
MP->>CTRL : GET /article/show?id=...
CTRL->>SVC : buildArticleShowData(id)
SVC-->>CTRL : {title,content,...}
CTRL-->>MP : {article, defined}
MP->>WXML : 绑定 article.title/content
WXML->>HTML : content="{{article.content}}"
HTML-->>WXML : 渲染富文本
MP->>MP : onShareAppMessage(title)
文章数据模型与字段
- 文章模型(Article)
- 表名:article
- 列表格式化:image→附件URL、defined→键值对、created_at→Y-m-d
- 附加字段:add_time_short、time、name、description、url、favorites、cate_info
- 多语言字段:title、content、description、keywords(随当前语言覆写)
- 分类模型(ArticleCategory)
- 提供 tree() 等方法用于构建分类树与导航。
- 列表项字段(Presenter)
- id、category_id、title、defined、image、created_at、add_time_short、time、click、description、url、cate_info(category_id/name/url)
classDiagram
class Article {
+int category_id
+int click
+attachment image
+defined defined_pairs
+datetime created_at
+string[] translatable
+string[] appends
+published()
+filterByCategory(catId)
+filterByArchive(archive)
+applyDefaultOrder()
+paginate(pageSize, page, url)
}
class ArticleCategory {
+tree(catId)
+whereKey(id)
}
Article --> ArticleCategory : "with('category')"
SEO 与结构化数据
- 页面标题、关键词、描述由 SeoResolver 基于分类与文章内容生成。
- 文章页注入 Article 类型 JSON-LD,包含 headline、image、datePublished、author、publisher、mainEntityOfPage 等。
依赖关系分析
- 控制器依赖服务与 SEO 组件,服务依赖模型与 Markdown 渲染器。
- 小程序依赖 http 服务与 mp-html 组件。
- 模型依赖 ORM、附件系统、多语言与时间格式化。
graph LR
MP_TS["article.ts"] --> HTTP["http 服务"]
MP_WXML["article.wxml"] --> MPHTML["mp-html 组件"]
CTRL["ArticleController"] --> SVC["ArticleService"]
SVC --> MODEL_A["Article"]
SVC --> MODEL_C["ArticleCategory"]
CTRL --> SEO["SchemaService"]
性能与缓存策略
- 列表性能
- 使用 with('category') 批量加载分类,避免 N+1 查询。
- 分页限制返回条数,摘要截断减少传输体积。
- 附件 URL 在模型层统一转换,避免重复计算。
- 图片优化
- 建议使用 mp-html 的图片懒加载与自适应尺寸;服务端可结合云存储或 CDN 提供缩略图与压缩版本。
- 内容预加载
- 列表页可预取下一页首条文章的关键字段(id/title/image),提升跳转体验。
- 离线缓存
- 列表数据可使用本地存储短期缓存(如最近浏览的分类列表),结合版本号或时间戳控制失效。
- 富文本渲染
- 将内容在服务端渲染为 HTML 后下发,减少小程序端解析开销;必要时在 mp-html 中启用增量渲染。
- 点击统计
- 详情页在控制器中异步更新点击量,避免阻塞渲染。
用户体验设计
- 阅读进度保存
- 在小程序端监听滚动事件,将当前滚动位置写入本地存储;返回时恢复进度。
- 字体大小调整
- 提供字体大小设置,持久化至本地存储;在 mp-html 中通过 CSS 变量或样式类切换。
- 夜间模式
- 通过主题切换改变背景与文字颜色;mp-html 支持自定义样式,可注入夜间主题。
- 分享体验
- 设置合理的分享标题与封面图;确保分享卡片信息准确。
常见问题与排错
- 大文件图片优化
- 问题:大图导致加载慢、内存占用高。
- 解决:服务端压缩与裁剪;小程序端懒加载;CDN 加速;按需加载缩略图。
- 富文本安全过滤
- 问题:XSS 风险。
- 解决:服务端对内容进行白名单过滤;mp-html 启用安全模式;避免执行脚本。
- 移动端兼容性
- 问题:不同机型渲染差异。
- 解决:使用响应式布局;测试主流设备;在 mp-html 中适配样式。
- 列表分页异常
- 问题:页码错误或数据为空。
- 解决:检查路由参数与分页配置;确认分类与归档条件;查看日志定位。
- 分享标题为空
- 问题:分享标题未设置。
- 解决:确保 onLoad 时正确设置 wx.setStorageSync('shareTitle');检查 API 返回字段。
结论
DouPHP 的文章模块在前端控制器、服务层与模型层形成了清晰的职责划分:控制器负责路由与视图数据组装,服务层专注业务逻辑与数据格式化,模型层提供高效的数据访问与多语言支持。小程序端通过 mp-html 渲染富文本,配合 http 服务与分享机制,提供了良好的阅读与传播体验。结合本文的性能优化与用户体验建议,可进一步提升文章页面的加载速度、可读性与可维护性。
附录:数据模型与字段说明
- 文章字段
- id:主键
- category_id:分类 ID
- title:标题(多语言)
- content:内容(Markdown,服务端转 HTML)
- description:描述(多语言,用于 SEO 与摘要)
- keywords:关键词(多语言,用于 SEO)
- image:封面图(附件 URL)
- defined:自定义属性(键值对)
- created_at:创建时间(格式化)
- click:点击量
- 列表项字段(Presenter)
- add_time_short:短显示时间
- time:时间标签
- name:文章名称(可能为别名或标题)
- url:详情页链接
- cate_info:分类信息(category_id/name/url)
- 分类字段
- name:分类名称(多语言)
- keywords:分类关键词(多语言)
- description:分类描述(多语言)
- parent_id:父分类 ID