不复制一套移动端视图:把响应式层重做一遍

给一套跑了多年的管理后台补移动端,最常见的做法是再做一套 m.* 视图:复制一份模板、改改布局,按 UA 分流出不同的控制器。

这个方案看着干净,实际代价很高——这个项目有 488 个视图,复制一份就是 488 个新的维护点,之后每个功能改动都要改两遍。

这次换了个做法:不加视图,只重做响应式层。三个文件、一次断点调整,覆盖从 320px 手机到桌面的全部宽度。过程里踩的坑比预想的多,记一下。

一、先确认框架支不支持「一套模板」

动手前先查清两件事,否则后面的判断全是猜的:

  • 内核本来就支持响应式:$config->devicePrefix['mhtml'] = '' ——这行配置等于关掉了”按设备前缀找另一套视图”的机制,所以同一个控制器、同一套模板天生服务所有设备。
  • 已有的移动样式是”补丁级”的:只有寥寥几条规则,覆盖不到真实的移动使用场景。

结论很明确:框架没拦你,是样式层没做完。所以这次不新增视图,只补样式层,并且把它补到位。

顺带说一句取舍:如果框架本身不支持响应式(比如两套完全不同的模板体系),那复制视图是合理的;但能加一层就别复制一份——维护成本差一个数量级。

二、断点要放在「布局真正切换」的位置

第一版只改了几条显而易见的规则,结果768–1024px 这一段仍然横向溢出——顶部导航已经堆叠了,内容区却还在用 1280px 的桌面布局。

根因是断点选错了:原来的断点按”常见手机宽度”设(≤767px),但那套桌面布局的最小可用宽度其实是 1280px 附近。768 到 1279 这一段既吃不到移动样式、又撑不起桌面双栏,就是夹缝。

修法是把断点扩到 1279px——只要视口窄于桌面双栏的可用宽度,就走单列。断点不是按设备型号定的,是按你的布局在哪一刻会塌定的。

配套的几处布局调整:

  • .main-row > .main-col/.side-col 统一堆叠(不靠 float 或固定宽度);
  • .table-form 的标签与控件改成纵向(原来并排,窄屏下控件被压到几十像素);
  • #sidebarHeader 独占一行,不再和内容抢宽度。

三、内联样式会盖掉你的 CSS

这一条最费时间:顶部二级导航被推到屏幕外,而 CSS 里明明写的是 width: 100%。

查下来是桌面脚本在运行时往 #subNavbar .nav 上写了内联的 left 和 max-width——内联样式的优先级高于样式表里的任何选择器(除非用 !important)。所以移动端必须显式解除:

1
2
left: auto;
max-width: none;

同理 #pageNav / #pageActions 原来的绝对定位,在窄屏下会压在二级导航上,也要在移动断点里改成静态定位。

教训是:改老项目的响应式,先搜一遍 JS 里有没有写内联 style。CSS 写得再对,也打不过运行时注入的内联值。

四、三种内容要单独降级

有些内容不是”换个宽度”就能用的,得换表达方式。

宽表格。 后台的列表页普遍是 8–15 列,窄屏只能横向裁切,看起来像”内容丢了”。除了让表格容器可以横滚,更重要的是给表格加一句提示(”左右滑动查看更多”),否则用户以为数据不全。

流程图。 这个项目的流程图是一张宽表,手机上被截得只剩前两列。处理方式是同一个 DOM 用两套渲染:≤767px 用 ZUI 样式渲染成”角色卡片 + 步骤列表”,平板及以上保留表格。注意是同一份数据换渲染,不是再做一份模板。

固定底栏。 .table-footer.fixed-footer 宽度写死 1318px 且固定定位,窄屏下直接压住表格最后两行。改成表格之后的普通批量操作栏——窄屏不需要”吸底”,需要”看得见”。

窄组件。 登录页的安全提示条固定 520px,320px 下超宽。改成流式布局(max-width: 100%)。

五、验证:用视口宽度量,不靠眼睛

“看起来不错”是最不可靠的验收方式。可量化的判据只有一条:

1
document.documentElement.scrollWidth === window.innerWidth

也就是页面不产生横向滚动。按 320 / 390 / 768 / 1024 / 1279 / 1280px 六个宽度逐个量,全部相等才算过;1280px 这一档还要额外确认 col-main + col-side 双栏恢复。

页面清单不能只看首页:我的地盘、我的任务、产品需求列表、项目任务、Bug 列表、任务详情、四类创建表单、指派/完成/关闭弹窗、登录页——表单和弹窗最容易漏,它们往往有自己独立的宽度约束。

六、两个环境坑

补响应式离不开真机数据,于是又踩了两个环境问题。

库版本和代码版本不一致。 现有联调环境的数据库停在旧版本、代码是新版本,一进首页就被重定向到升级页 —— 布局根本测不了。用环境自带的 CLI 通道做一次幂等升级才继续。

远程测试库结构太旧。 另一个环境的库还缺新版本新增的列,列表页直接抛 SQL 错误,导致”最新动态”区块被截断——看起来像布局问题,其实是数据取不出来。

还有一个纯浪费时间的:我只在项目目录里找容器配置,没先看已有的联调环境,白起了一套 podman。删掉之后改用现成的环境复验。搭环境前先问一句”是不是已经有了”,比什么都省事。

总结

这次改动的全部产出是三个文件:一个样式表、一个移动端头部 hook、一个公共 header 模板。没有新增任何视图。

可以复用的几条:

  1. 先确认框架支不支持一套模板——支持就别复制视图,维护成本差一个数量级;
  2. 断点按”布局会在哪一刻塌”来定,不是按设备宽度定;夹缝区间最容易漏;
  3. 改响应式前先搜 JS 里写内联 style 的地方,内联值会盖掉所有 CSS;
  4. 宽表格、流程图、固定底栏要换表达方式,不是调宽度;
  5. 验收用 scrollWidth === innerWidth 量,并且覆盖表单和弹窗。

最后一条:移动端不需要”和桌面一样”,只需要”在手机上能完成同一件事”。想清楚这一点,很多”必须复制一套视图”的需求会自动消失。