打开一个用 Misago 搭的论坛,浏览器要做两次同样的事。Django 先把整页 HTML 拼好,连帖子列表都塞进一份 JSON 一起吐给你。JS 下载完成后,React 读这份 JSON,把刚渲染出的 HTML 整块换掉。

Misago 维护者受够了这套双份渲染。项目计划分批把 React 移出代码库,改用 HTMX 做局部更新,不是一次性推翻重写,而是先啃掉部分页面,其余页面接受一段"点链接就刷新整页"的过渡期。

双份渲染,代价在哪

现在的路径是这样:请求进来,Django 视图渲染出几乎完整的 HTML,同一份数据顺手打包成 JSON 埋进页面。JS 加载完,React 把这块 HTML 整个换成组件版本。

代价很实在。几乎每个页面要写两遍模板,Django 模板一份,React 组件一份。路由、翻译文件(django.podjangojs.po 各存一份)、插件扩展点,都要照顾两套。

维护者提到过一个真实的坑:有人改了 Django 模板,页面先闪一下改动,马上又被 React 覆盖回原样。他不知道自己还需要改另一份组件——这种"改了却没生效"的调试成本,比包体积数字更容易被低估。

对维护多插件生态的开源论坛项目来说,这类双头维护会随插件数量增加,不是一次性成本,是长期税。

两条渲染路径 React 双渲染(现状) 请求进入 Django渲染HTML+JSON JS下载完成 React整段替换HTML 两遍模板 · 两遍翻译 · 两遍维护 HTMX 局部渲染(计划) 交互触发(点击/切换) Django只返回局部HTML HTMX原地替换该区域 一份模板 · 一次渲染 · 无需JSON

HTMX 换的是什么,没换的是什么

HTMX 的做法是把页面上会变化的区域标成"孤岛"。交互发生时,浏览器只问服务端要这一小块新 HTML,换掉对应区域,其余部分不动。切换帖子分类,后端判断请求来自 HTMX,只返回帖子列表那一段,不用序列化 JSON,不用写专门的 React 组件。

这个思路不新。二十年前 jQuery $.get() 换局部内容,或者 Rails 的 Turbolinks,做的是同一件事,HTMX 只是换了个更声明式的写法。古人说"反者道之动",论坛这类产品绕回局部刷新,倒有点这个意思——走了一圈复杂架构,又回到了更朴素的路数。

迁移是分批推进的,不是一次切换:

阶段misago.jsgzip 后
迁移前615KB124KB
账户设置页迁移后578KB107KB
帖子列表页迁移后530KB99KB
vendor.js(未变动)679KB

vendor.js 的 679KB 基本没动,说明这件事离整体完成还远。维护者当时的计划是完整迁移排到 2024 年之后,管理后台不动,继续用 Django 通用视图。这是 2023 年的讨论材料,目前公开信息只能确认部分页面已经切换,没有证据说明整站迁移已经收尾。

过渡期里,部分页面既没了 React,也还没接上 HTMX,点链接、提交表单就老实刷新整页。只有点赞、投票这类刷新页面完全不能忍的交互,才专门保留异步处理。

misago.js 一路缩水 原始版本 615KB 账户设置迁移后 578KB 帖子列表迁移后 530KB gzip后:124KB → 107KB → 99KB,vendor.js的679KB还没动

这笔账谁该算,谁不用跟

这套思路对论坛类产品站得住。交互点状,数据大头在服务端,SPA 换来的"无刷新"体验,没盖住双份渲染、双份翻译、插件写两遍的维护成本。

但别把这事读成"HTMX 优于 React"的通用结论。一个需要复杂客户端状态、频繁跨页面数据联动的产品,比如在线文档编辑器、看板协作工具,HTMX 很难覆盖这类场景,SPA 该用还得用。Misago 做的是重新算一遍论坛这种产品的复杂度账,不是给所有前端架构判死刑。

包体积从 615KB 降到 530KB,gzip 后从 124KB 到 99KB,数字是真的,但不等于用户体验立刻变好。服务端响应速度、缓存策略、网络条件,才是决定加载体验的关键变量,这些材料里都没有实测数据,只能说目前还看不清。

对正在维护 Django、Rails 这类服务端渲染项目、又被 SPA 双份渲染和前端构建链拖累的团队,Misago 的路径值得参考,更适合先在低风险页面小范围试点,而不是照抄整站重写。对已经用 React 或 Vue 做复杂交互面板的团队,这篇材料不构成换栈理由。

接下来该盯的是三件事:vendor.js 的 679KB 什么时候真正下降,过渡期的整页刷新体验有没有引发用户抱怨,完整迁移是不是真按计划挤进了 2024 年之后。这几项目前都没有公开的后续确认。

论坛先把架构做重了一遍,又轻装绕回去。技术选型这件事,复杂度从来不是免费的,兜兜转转最后还是要照实结账。