3.1 模板与 CSS 的组织规范
> 让改动只落在该落的文件里,内核升级时不被人打回来。
模板不是自由格式,它先要变成 PHP
Discuz! 的模板文件(.htm,X5 的 default 套系里更多用 .php 后缀)不是浏览器直接读的 HTML。它先被 source/class/class_template.php 解析一遍,模板标签被翻译成 PHP,编译结果缓存进 data/cache/,后续访问直接跑缓存文件;缓存失效才重新编译。
所以模板是一套受限的伪代码:外面包着 HTML,里面嵌着会变成 PHP 的逻辑。官方对这种混合体单独立了一套书写规范,目的只有一个——让任何模板文件都能被人一眼看懂,并且能被工具稳定地解析。
标记写法的硬规矩
| 规矩 | 正确写法 | 常见错法 |
|---|---|---|
| 标记一律小写 | <div class="wp"> |
<DIV CLASS="wp"> |
| 参数值用双引号 | <img src="{IMGDIR}/a.png" /> |
<img src='{IMGDIR}/a.png'> |
| 非成对标记以斜杠收尾 | <br /> <img ... /> |
<br> <img ...> |
| 属性之间单个空格分隔 | <a href="#" title="x"> |
<a href="#" title="x"> |
| 布尔属性不写值 | <input type="checkbox" checked /> |
<input type="checkbox" checked="checked" /> |
| 行内样式尽量移到 CSS | <div class="t"> |
<div style="color:red"> |
这几个示例里最容易被违反的是"非成对标记以斜杠收尾"。Discuz! 的模板解析器对标记的处理有自己的实现习惯,XHTML 风格的写法在整个代码库里是一致的,你跟着写就不会在某个边角翻车。
逻辑体必须被注释包起来
真实模板文件里,所有逻辑体都写成 HTML 注释的形态——起止各一层:
<!--{if $uid}>
<div class="userbar">欢迎回来,{$username}</div>
<!--{else}-->
<div class="userbar"<a href="member.php?mod=logging&action=login"></a></div>
<!--{/if}-->
去掉注释壳之后,就是我们常说的 {if}、{/if} 标签。写成注释有两个实际好处:一是模板里的 HTML 在编辑器里仍然可读、可折叠;二是万一某处解析失败,输出到页面上的是一段注释而不是一堆乱码。
两个例外值得记住:
{if}条件嵌在 HTML 属性里时可以不写注释壳,例如<div class="{if $uid}on{/if}">;{subtemplate xxx/yyy}和{template xxx/yyy}这类调用标签后面不带斜杠,也没有结束标签。
缩进用 TAB,且只表示逻辑层级
规范要求缩进使用制表符(TAB),且缩进只用来表达逻辑嵌套深度——一层 {if} 或 {loop} 就多缩进一级,进入循环体、离开循环体再退回来。它不是用来对齐某个具体像素位置的。
<div class="threadlist">
<!--{loop $threadlist $tid $thread}-->
<div class="thread">
<a href="forum.php?mod=viewthread&tid={$tid}">{$thread[subject]}</a>
</div>
<!--{/loop}-->
</div>
同层级的兄弟节点不靠空行或额外缩进来分组,缩进一变就意味着逻辑层级变了。这条规则看着死板,实际好处是:模板被解析器编译成 PHP 之后,缩进与生成的 PHP 结构一一对应,出问题时看缓存文件就能定位。
顺带一提,官方明确不建议用 DreamWeaver、Fireworks 这类可视化工具生成或编辑 Discuz! 模板。它们会自作主张把标签改写、补属性、加 &nbsp;,一次保存就能把你的规范打乱。要编辑就用纯文本编辑器。
CSS 的两层扩展机制
Discuz! 不要求你重写全部样式。default 套系里已经有一份编译好的 common.css(公共部分)和 module.css(各模块部分),你的套系只需要写扩展文件去做覆盖式追加:
| 扩展文件 | 覆盖的对象 | 典型内容 |
|---|---|---|
template/{标识}/common/extend_common.css |
default 的 common.css |
全站字体、颜色、按钮、导航、分页 |
template/{标识}/common/extend_module.css |
default 的 module.css |
论坛列表、帖子正文、门户区块 |
/* extend_common.css —— 只写你要改的,其余全部继承 */
.wp { width: 1200px; margin: 0 auto; }
.pg { border: 1px solid #E5E5E5; }
.pg a { color: #2B8FD4; text-decoration: none; }
.pg a:hover { text-decoration: underline; }
文件被追加在默认样式之后加载,同选择器、同权重时你的规则胜出。好处是内核改版时你不需要跟着一层层比对。
class 命名:给内核留出安全距离
内核 CSS 里的类名大多很短:wp、pg、tl、f……你如果也给自定义样式起同名类,改一次就把整站布局掀了。做法是加一个自己的前缀,全站统一:
/* 前缀 dt,语义化命名,和内核类零冲突 */
.dt-card { border-radius: 4px; }
.dt-card-hd { padding: 12px 16px; font-weight: bold; }
.dt-list-item:hover { background: #F7F7F7; }
命名带上组件和元素两段(card + hd),半年后你自己回来改也知道动的是哪一块。改之前先在 template/default/common/ 里搜一下同名词,确认没撞车。
一张表说清"这文件该放哪"
| 你要做的事 | 落点 | 生效方式 |
|---|---|---|
| 改首页、帖子页 HTML | template/{标识}/forum/ 下对应文件 |
后台 → 界面 → 更新缓存 |
| 改公共头尾 | template/{标识}/common/header、footer |
同上 |
| 改全站基础样式 | template/{标识}/common/extend_common.css |
覆盖 common.css |
| 改单个模块样式 | template/{标识}/common/extend_module.css |
覆盖 module.css |
| 换主色调(想可切换) | template/{标识}/style/t1/style.css |
后台风格页选扩展配色 |
| 换界面基础图片目录 | 后台风格页的界面基础图片目录 | 写入 $_G['style']['imgdir'] |
| 改手机版页面 | template/{标识}/touch/ |
与 PC 版互相独立 |
| 加门户 DIY 区域 | template/{标识}/portal/diyxml/ |
后台 DIY 导入 |
落地流程
改模板的顺序固定成这样,几乎不会返工:
- 开开发者模式(
config/config_global.php末尾加$_config['plugindeveloper'] = 1;),后台才会出现设计模板的入口; - 复制
template/default里你要改的那几个文件到自己的template/{标识}/下,只改复制出来的那几份,不动default; - 页面结构改模板文件,样式改
extend_common.css/extend_module.css,一个像素的问题不要往模板里塞内联style; - 后台 → 界面 → 更新缓存,再刷新页面。
这四步里最容易被跳过的是第 4 步。模板有编译缓存,改完不更新缓存你会以为代码没生效,然后开始瞎改——其实改的是缓存。
