DedeCMS 列表页文章如果没上传缩略图,默认会显示一张通用占位图(defaultpic.gif)——一个灰白底蓝字 "暂无图片" 的丑图。在追求美观的现代列表页布局里,这种占位图比"什么都不显示"还糟糕。要做到"有图就显示、无图就隐藏 dt 容器"看似简单,实际上 DedeCMS 模板引擎的 runphp / @me / 单双引号陷阱里有一连串小坑等着踩。
这一篇把 "DedeCMS 列表页缩略图条件显示" 从模板引擎的工作机制讲到几种实战写法、CSS 现代化替代、SEO 影响、跨版本兼容与 FAQ。每段代码都标明了适用版本(V5.6 / V5.7 / V5.7 SP2 / DedeBIZ)。
arc.listview.class.php 内部缩略图判定逻辑
原文提到的关键代码在 /include/arc.listview.class.php 大约第 883-885 行(V5.7 SP2 实际行号是 891-893,跨版本略有差异):
if ($row['litpic'] == '-' || $row['litpic'] == '') {
$row['litpic'] = $GLOBALS['cfg_cmspath'] . '/images/defaultpic.gif';
}这段代码的语义是:"如果数据库里 litpic 字段是空字符串或 '-',就把它替换成默认占位图路径"。数据库 litpic 字段在文章未上传图时填 '-' 或空——这是 DedeCMS 历史设计。
要做到"无缩略图时不显示而不是显示默认图",有两条路径:
- 改 PHP 源码:删除 / 注释掉这段 if 判断,让 litpic 保持空,模板里再判断;
- 不改源码,模板里识别 defaultpic:用
strpos(@me['litpic'], 'defaultpic')检查路径里有没有defaultpic字串,是的就视为无图。
两种各有优劣。改源码彻底但升级会被覆盖;模板判断字符串需要 PHP 文件里默认图路径恰好含 "defaultpic"——大部分 DedeCMS 站符合这个特征。生产推荐方案 1(改源码 + 子模板覆盖),方案 2 适合"不能改源码但要快速生效"的场景。
DedeCMS 模板里 runphp 的工作机制
DedeCMS 模板支持两种"嵌入 PHP" 的语法:
| 语法 | 用途 | 变量名 |
|---|---|---|
[field:array runphp='yes']...[/field:array] | 在 {dede:arclist} 循环内引用整行数据 | @me 是关联数组(可访问 @me['litpic']) |
{dede:field.litpic runphp="yes"}...{/dede:field.litpic} | 专门处理某一个字段 | @me 是字段值(直接是字符串) |
{dede:php}...{/dede:php} | 任意 PHP 代码 | 无固定变量 |
这三种语法在 DedeCMS 编译模板时被转成 <?php ... ?> 块嵌进生成的 PHP 文件。编译产物缓存在 /data/tplcache/,所以改完模板要在后台清缓存(系统 → 系统设置 → 更新缓存 → 模板缓存)。
@me 变量的实际含义
@me 不是 PHP 原生语法,是 DedeCMS 模板编译时的占位符。编译后 @me['litpic'] 变成 $row['litpic'] 之类的 PHP 引用。所以模板里的 @me 必须严格按 PHP 数组访问语法写,包括引号和花括号。
单双引号陷阱的真实原因
原帖警告"单双引号问题,只能统一用单引号"——这条警告是真的,但原因没说清。看这段代码:
[field:array runphp='yes']
@me=(strpos(@me['litpic'],'defaultpic')?'':"
<dt><a href='{@me['arcurl']}' title='{@me['seotitle']}'><img src='{@me['litpic']}' alt='{@me['title']}'/></a></dt>
");
[/field:array]整个 [field:array runphp='yes']... 标签的属性 runphp='yes' 用单引号——这是 DedeCMS 模板解析器的硬性要求(双引号会被认为是属性结束)。所以内部代码里所有"看起来该用双引号"的地方都得改单引号。
更深层的原因:DedeCMS 模板编译器把 [field:array] 标签转成 PHP eval 调用,参数是字符串。如果内部含没转义的双引号,eval 字符串提前结束,剩下代码作为乱码触发 PHP 错误,DedeCMS 容错时会返回 array 提示"出错"——就是原帖说的"返回 array"现象。
复杂 HTML 内嵌的写法
如果实在需要双引号(比如 HTML 标签),用 " HTML 实体:
@me=" ";或者用 chr(34):
$dq = chr(34);
@me="...";几种"无图不显示"的写法对比
方法 A:改源码 + 模板里直接判断空
删 arc.listview.class.php 里的默认图替换逻辑后,模板:
{dede:field.litpic runphp="yes"}
if (!empty(@me) && @me != '-') {
@me = "<dt><img src='{@me}' alt='{@me['title']}'/></dt>";
} else {
@me = "";
}
{/dede:field.litpic}优点:清晰直观;缺点:改源码升级被覆盖。
方法 B:不改源码 + 模板识别 defaultpic 字符串
{dede:field.litpic runphp="yes"}
@me = (strpos(@me, 'defaultpic') !== false ? '' : "<img src='" . @me . "'/>");
{/dede:field.litpic}优点:不动源码升级安全;缺点:依赖默认图路径含 defaultpic 字符串。如果站长改了默认图文件名(比如改成 placeholder.png),这条规则失效。
方法 C:用 [field:array]
[field:array runphp='yes']
@me = (strpos(@me['litpic'], 'defaultpic') !== false
? ''
: "<dt><a href='{@me['arcurl']}' target='_blank'><img src='{@me['litpic']}' alt='{@me['title']}'/></a></dt>");
[/field:array]这是原帖给的版本。优点:可访问当前文章的所有字段(arcurl、title、seotitle 等);缺点:单双引号陷阱多。
方法 D:CSS 现代化替代
不改 PHP 也不写 runphp,用 CSS 控制:
<dt class="thumb">
<img src="[field:litpic/]" alt="[field:title/]"
onerror="this.parentElement.style.display='none'">
</dt>优点:极其简单,无需懂 DedeCMS 模板;缺点:依赖 JS(无 JS 用户看到默认图),且 onerror 触发时机有时不可控。
方法 E:把默认图本身做成透明 SVG
最优雅但最少人想到的方案——把 /images/defaultpic.gif 替换成 1×1 透明 PNG 或空白 SVG。这样 PHP 层 if 判断照样跑,模板里拿到的是"看起来空"的图。配合 CSS 的 img:empty 或 img[src=""] 选择器隐藏 dt。
/* 1×1 透明的图片自动隐藏父级 dt */
.thumb img[src*="defaultpic"] {
display: none;
}
.thumb:has(img[src*="defaultpic"]) {
display: none; /* 父级也隐藏(CSS :has 在现代浏览器支持) */
}优点:纯 CSS,无 JS 依赖,无需改模板;缺点:需要 CSS :has 选择器支持(2023 年起所有现代浏览器支持)。
各方案性能对比
| 方案 | 每篇文章额外开销 | 适用场景 |
|---|---|---|
| A 改源码 + 模板判断空 | 0(无 runphp) | 能改源码的项目 |
| B 模板识别 defaultpic | ~0.05ms(runphp + strpos) | 不能改源码 |
| C [field:array] 完整版 | ~0.1ms(runphp + 数组访问) | 需要访问多字段 |
| D JS onerror | ~5ms(DOM 操作) | 急救快速上线 |
| E CSS :has | ~1ms(选择器匹配) | 现代浏览器项目 |
对中等规模站点(单页 20 条文章),方案 A/B/C/E 都在 1-5ms 总开销内,肉眼无差。方案 D 因为是浏览器端事件触发,会有轻微闪烁(默认图加载失败再隐藏)。
跨 DedeCMS 版本兼容
不同版本里 arc.listview.class.php 的关键行号略有差异:
| 版本 | 判定缩略图的代码位置 | 默认图路径 |
|---|---|---|
| DedeCMS V5.6 | ~ 第 845 行 | /images/defaultpic.gif |
| DedeCMS V5.7 | ~ 第 875 行 | /images/defaultpic.gif |
| DedeCMS V5.7 SP1 | ~ 第 880 行 | 同上 |
| DedeCMS V5.7 SP2 | ~ 第 891 行 | 同上 |
| DedeBIZ V6.x | ~ 第 905 行 | 同上 + 后台可配置开关 |
所有版本的判定逻辑和默认图路径基本一致,本文写的几种方案在所有版本都通用。DedeBIZ 已经在后台加了"无图时是否显示默认图"的开关,新装站点可直接走 DedeBIZ 省掉模板改造。
SEO 影响考量
"无图不显示" 这个改造对 SEO 的影响:
用户体验提升
整页布局更美观——无图的文章不会出现破碎的灰色占位,列表页视觉一致性提升,用户停留时间和点击率多数会上升 5-15%。这是 Google Core Web Vitals 之外的"用户行为信号",间接帮排名。
不要把所有图都隐藏
如果站点 70% 文章都没图,列表页就变成"几个图 + 大片空白"——视觉不平衡。这种情况建议:① 给文章批量补封面图(用首图自动提取或 AI 配图);② 改用纯文本列表,不展示缩略图位。
与 lazy load 协同
显示出来的缩略图加 loading="lazy":
<img src='[field:litpic/]' alt='[field:title/]' loading="lazy" decoding="async">对列表页 LCP 性能贡献明显——只有视口内的图实际加载,下面没看到的图懒加载。
alt 属性必填
给图片加 alt 是图片 SEO 基础。DedeCMS 默认 alt 是 [field:title/]——文章标题。这是合理的,但更优是用 [field:keywords/] 或 [field:description/],让 alt 更精准。
升级时的覆盖问题
方案 A 改了 arc.listview.class.php,DedeCMS 升级时会被覆盖。三种保护:
- 升级前 git diff 备份,升级后手动 merge;
- 把改造放到独立的 hook 文件(DedeBIZ 提供事件 hook 机制,可在不动核心文件的情况下注入逻辑);
- 切到 DedeBIZ,用后台开关代替源码改动。
与现代 CMS 的等价做法
如果未来要把 DedeCMS 站迁出,"无图不显示" 的等价做法:
| CMS | 等价做法 |
|---|---|
| WordPress | 主题 PHP if (has_post_thumbnail()) the_post_thumbnail(); |
| Hexo | 模板 {% if post.thumbnail %}...{% endif %} |
| Hugo | {{ if .Params.thumbnail }}...{{ end }} |
| Typecho | 模板 <?php if ($this->fields->img): ?>...<?php endif; ?> |
所有现代 CMS 都把"是否有缩略图"做成了原生模板能力,不需要 strpos 这种字符串技巧。
批量从正文首图提取写回 litpic 字段
很多 DedeCMS 站点的痛点是历史文章 litpic 大量为空——当年发布时没设缩略图。要批量补,最实用的做法是从 article body 里提取第一张 <img> 标签的 src 写回 litpic:
// 一次性脚本:扫所有 litpic 为空的文章,从正文提取首图回写
require_once 'include/common.inc.php';
$db = $GLOBALS['dsql'];
$rows = $db->GetAll("
SELECT a.id, a.body
FROM dede_archives ar
JOIN dede_addonarticle a ON a.aid = ar.id
WHERE (ar.litpic = '' OR ar.litpic = '-' OR ar.litpic IS NULL)
AND a.body IS NOT NULL
");
$updated = 0;
foreach ($rows as $row) {
if (preg_match('/<img[^>]+src=[\'"]([^\'"]+\.(?:jpg|jpeg|png|webp))[\'"]/', $row['body'], $m)) {
$litpic = $m[1];
// 转相对路径
$litpic = preg_replace('#^https?://[^/]+#', '', $litpic);
$db->ExecuteNoneQuery("
UPDATE dede_archives SET litpic = '$litpic' WHERE id = {$row['id']}
");
$updated++;
}
}
echo "更新 $updated 篇文章的 litpic\n";这段代码跑完之后大部分历史文章会有 litpic——配合本文的 "无图不显示" 改造,列表页视觉效果会大幅提升。
配套生成缩略图
从正文提取的图通常是原始大图(比如 1920×1080),直接用作缩略图浪费带宽。建议同时生成对应的小尺寸缩略图:
// 配合本文的 image.helper.php 改造,对每张回填的 litpic 生成 240×160 缩略图
require_once 'include/helpers/image.helper.php';
$thumb_dir = '/uploads/thumbs/';
foreach ($rows_with_new_litpic as $row) {
$original = DEDEROOT . $row['litpic'];
$thumb_path = DEDEROOT . $thumb_dir . basename($row['litpic']);
if (!file_exists($thumb_path)) {
ResizeImg($original, $thumb_path, 240, 160);
}
// 回写到 litpic 字段为缩略图路径
}和图片 SEO 联动的最佳实践组合
"无图不显示" 是单点优化。配合下面几个改造能让 DedeCMS 列表页 SEO 提升一个量级:
- 给每张缩略图加完整 alt:alt 用
[field:title/]或更精准的[field:description/]; - 用 WebP 格式替代 JPG / PNG:体积减小 25-40%,加载更快;
- 缩略图 lazy load:
loading="lazy"让首屏外的图延后加载; - 生成 image sitemap:在 sitemap.xml 里加 image:image 节点,让 Google 图片搜索能发现;
- 缩略图 URL 含语义:用
{文章标题拼音}-thumb.webp而不是1234567890-thumb.jpg; - 响应式图片:用
srcset给不同屏幕给不同大小的图:
<img src='[field:litpic_240/]' alt='[field:title/]'
srcset='[field:litpic_240/] 240w,
[field:litpic_480/] 480w,
[field:litpic_960/] 960w'
sizes='(max-width: 600px) 100vw, 240px'
loading='lazy' decoding='async'>每个尺寸的缩略图都需要预先生成(用 image.helper.php 改造或后处理脚本)。
常见问题解答
改完模板前台没生效,怎么办?
清模板缓存。后台 → 系统 → 系统设置 → 更新缓存 → 模板缓存。或者手动删 /data/tplcache/ 目录下所有文件。模板编译产物清空后下次访问会重新编译。
defaultpic.gif 改名后方案 B 失效,怎么办?
方案 B 依赖路径里的 "defaultpic" 字符串。改名后改判断条件,比如 strpos(@me, 'placeholder')。或者改回方案 A——直接改源码删默认图替换逻辑,模板里判断空字符串。
runphp 写错导致整个列表页报错怎么办?
检查三处:① 单双引号是否统一为单引号;② @me 用法是否正确(field:array 里 @me 是数组、field.litpic 里 @me 是字符串);③ 整体结构是否完整闭合([field:array]...[/field:array])。在 PHP error_log 里能看到具体错误行号。
多列表(dede:arclist 嵌套 dede:list)的缩略图判定有什么差异?
无差异——arclist 和 list 都共用 arc.listview.class.php 的判定逻辑。区别在于上下文变量:arclist 里的 [field:xxx] 引用当前 article,list 里嵌套时小心变量作用域不要混。
改造后能正常生成 sitemap.xml 吗?
能。模板层的图片显示与否不影响 sitemap 生成——sitemap 直接从数据库 litpic 字段读取生成 image:image 节点。如果你想让"无图文章不进 image sitemap",要单独改 sitemap 生成代码(/data/sitemap.xml 生成器)。
能不能给"有图"和"无图"的文章用不同的 CSS 样式?
能。在 dt 元素加 class:
{dede:field.litpic runphp="yes"}
if (strpos(@me, 'defaultpic') !== false) {
@me = "<dt class='no-thumb'></dt>";
} else {
@me = "<dt class='has-thumb'><img src='" . @me . "'/></dt>";
}
{/dede:field.litpic}CSS 里就可以 .no-thumb 给完全不同的布局(比如纯文本卡片、彩色背景代替图等)。
列表里图片和文字应该上下排列还是左右排列?
看场景:① 内容站(博客 / 新闻)建议左图右文,符合 F 型阅读路径;② 产品站建议上图下文,让图本身成为主导;③ 移动端无论 PC 哪种布局都建议改成上下,左右在窄屏会挤压。用 CSS @media 切换。
能不能用 AI 自动给无图文章配图?
能。两条路:① 从正文提取首图:用 PHP 正则提取 article.body 里第一张 <img> 的 src,写回 litpic 字段;② 调用 AI 配图 API(DALL-E / Midjourney API / 国产文心一格 / 通义万相)按文章标题生成主题图。前者免费,对老站补图最实用;后者每张图 0.05-0.5 美元,适合新文章自动配图。
缩略图懒加载(loading=lazy)会影响 SEO 吗?
不会。Googlebot 自 2018 年起完全支持原生 lazy loading,能正确抓取懒加载的图。Core Web Vitals 的 LCP 指标反而会因为 lazy load 变好(首屏加载更快)。但 LCP 元素(首屏第一张图)不要 lazy load——它必须 eager 加载否则评分扣。
方案 A 改了源码升级被覆盖,能不能写 hook 而不动核心?
原版 DedeCMS V5.7 没有完整的 hook 机制——只有零星几个钩子。要做"不动核心"的扩展,要么改用 DedeBIZ V6.x(有了 plugin 体系),要么把改造做成独立 PHP include 在 common.inc.php 里加载。后者依赖 common.inc.php 不被覆盖(实际上 common.inc.php 升级也会被覆盖),所以最稳的办法是切 DedeBIZ 或迁移到现代 CMS。