3.4 图片资源与性能优化
> 图片路径写成常量,首屏少传 300KB,按收益排序逐条砍。
图片放在哪:两类目录两种命运
论坛里的图分两种来路,放的地方完全不同:
| 类型 | 目录 | 引用方式 | 归属 |
|---|---|---|---|
| 界面基础图片 | 后台风格页配置的界面基础图片目录(默认 static/image/common) |
{IMGDIR} / $_G['style']['imgdir'] |
跟着套系走 |
| 扩展图片 | 扩展图片目录(你的套系内) | {STYLEIMGDIR} / $_G['style']['styleimgdir'] |
跟着风格走 |
| 用户上传附件 | data/attachment/ |
由附件表记录生成链接 | 用户内容 |
| 头像 | 后台设置的远程调用或本地目录 | 模板头像变量输出 | 用户内容 |
两个目录的配置位置在后台 → 界面 → 模板风格里,和配色一起改。换套系时只有界面基础图片目录会整体替换,扩展图片目录是你自己的东西,所以自家 logo、品牌图一律放扩展图片目录。
不要硬编码路径
硬编码是模板开发里最常见也最难查的坑:
<!-- 错误:换套系、换域名、移动端都直接崩 -->
<img src="/template/我的标识/style/t1/images/logo.png" />
<!-- 正确 -->
<img src="{STYLEIMGDIR}/images/logo.png" alt="{$_G['site']}" />
第二条为什么必须这么写:
- 换套系不用改代码。 目录名写在配置里,不是写死在模板里。
- 换域名不用改代码。 Discuz! 会输出绝对地址,站点搬家或改域名不会丢图。
- CDN 友好。 后台可以把静态资源单独挂到 CDN 或独立域名,模板不用知道这件事。
在 .php 业务文件里同理,用 $_G['style']['styleimgdir']。唯一该硬编码的是静态目录常量,比如 DISCUZ_ROOT_STATIC(存放 md/txt/js/css/json/xml/图片等静态文件的目录)——它的存在意义就是对外提供静态资源路径,属于合理使用。
图片优化:从大到小的四刀
第一刀是尺寸。 上传 2000px 宽的图然后靠 CSS 缩到 200px 显示,是最常见的浪费。Discuz! 论坛附件本身就支持缩略图分类(后台 → 界面 → 上传设置里配置),发帖时先生成缩略图,模板里用缩略图地址,别引用原图。
第二刀是格式。 站点装饰用的图标、箭头、渐变这类纯图形,用 PNG 或 GIF 就够,不要用 JPEG——JPEG 在纯色块和文字周围会产生明显的振铃噪点。照片才用 JPEG。
第三刀是 CSS sprite。 一堆小图标合成一张图,用背景定位取:
.dt-ico-reply,
.dt-ico-share,
.dt-ico-fav {
background-image: url('{STYLEIMGDIR}/images/icons.png');
background-repeat: no-repeat;
display: inline-block;
width: 16px;
height: 16px;
}
.dt-ico-reply { background-position: 0 0; }
.dt-ico-share { background-position: -16px 0; }
.dt-ico-fav { background-position: -32px 0; }
代价是这张图会被第一个用到它的页面缓存住,后续页面直接命中缓存不再请求。十几个以内的小图标合成一张,收益明显;上百个就别合了,CSS 本身会变大,而且精灵图很难维护。
第四刀是懒加载。 首屏之外的图加 loading="lazy",一行代码的事:
<img src="{STYLEIMGDIR}/images/banner.jpg" alt="" loading="lazy" />
注意别对首屏图片加,首屏图一慢就是白屏,体验反而更差。
静态资源的版本号
浏览器缓存是双刃剑:CDN 和本地缓存让二次访问快得飞起,但资源更新后用户拿到旧版。解法是给引用加版本号,改了内容就换号:
<link rel="stylesheet" href="{STYLEIMGDIR}/style.css?v={STYLEVER}" />
<script src="{STYLEIMGDIR}/js/main.js?v={STYLEVER}"></script>
{STYLEVER} 是风格常量之一,取值来自后台风格页。这样同一次发版里,所有页面引用的版本号一致,缓存自然失效;没改的文件版本号不变,继续走缓存。比自己拼时间戳好——时间戳每次都变,等于把所有缓存全废了。
模板里的资源引入位置
CSS 和 JS 放在哪里,直接决定渲染过程会不会被卡住:
| 资源 | 位置 | 原因 |
|---|---|---|
| 关键 CSS | <head> 里 |
晚加载会导致首屏闪白 |
| 非关键 CSS | 可延后 | 不影响首屏视觉的可以晚点来 |
| 统计类 JS | 页面底部 | 加载慢也不影响阅读 |
| 需要立即执行的 JS | <head> 且不设 defer |
否则执行时机不对 |
| 其余 JS | 加 defer |
下载和解析不阻塞文档解析 |
defer 对大多数第三方脚本是安全的默认值,但表单校验、编辑器初始化这类必须在 DOM 就绪后同步执行的脚本不要加。加上之后表现不对,八成就是这个原因。
优先级表格
按收益从高到低排,动手时从上往下做,一轮下来通常就够了:
| 优先级 | 动作 | 预期收益 | 成本 |
|---|---|---|---|
| 1 | 列表页、门户页改用附件缩略图,不引原图 | 极大(首屏可能从数 MB 降到百 KB 级) | 低,改模板输出 |
| 2 | 给首屏外的图片加 loading="lazy" |
大 | 极低,一行属性 |
| 3 | 换图片格式(装饰图转 PNG/GIF) | 中 | 低,替换文件 |
| 4 | 小图标做 sprite | 中 | 中,要算坐标 |
| 5 | 非关键 JS 加 defer |
中 | 极低 |
| 6 | 静态资源加版本号 {STYLEVER} |
中(提升回访体验) | 极低 |
| 7 | 把装饰图移到扩展图片目录的子目录,按需启用 | 小 | 中 |
| 8 | 合并自定义 CSS/JS 减少请求数 | 小(HTTP/2 后更小) | 中 |
第 8 项放最后是有道理的:HTTP/2 之后多个小文件的请求开销已经很低,合并的收益远不如把原图换成缩略图。花两小时合并 CSS,不如花十分钟把帖子列表的附件换成缩略图。
验证手段
改完必须实测,别凭感觉。浏览器开发者工具的 Network 面板按 Size 排序,看首屏总共传了多少;再勾上 Slow 3G 限速刷新,观察首屏时间。前端资源之外,页面本身的模板编译缓存也要确认已经更新,否则你测的是缓存里的旧页面。
论坛上有个特殊场景值得单独留意:论坛首页的板块图标、版块描述图往往是站长自己上传的大图,很多站没做缩略图就直接贴原图。一张 300KB 的板块图乘以十几个板块,首屏就废了——这是最划算的一处优化点。
