DedeCMS列表页无缩略图不显示:arc.listview.class.php改造、runphp单双引号陷阱与CSS现代化方案

DedeCMS 文章列表页文章未上传缩略图时默认显示丑丑的 defaultpic.gif,怎么改成无图就完全隐藏?本文从 arc.listview.class.php 内部判定逻辑入手,给出改源码 / 模板 strpos 检测 / [field:array] runphp / CSS :has 选择器五种方案的完整代码、性能对比、单双引号陷阱真实根源、跨 V5.6/V5.7/SP2/DedeBIZ 版本兼容、SEO 与 lazy load 协同、AI 自动配图等延伸方案。

更新 24 分钟阅读 2,779 阅读

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 历史设计。

要做到"无缩略图时不显示而不是显示默认图",有两条路径:

  1. 改 PHP 源码:删除 / 注释掉这段 if 判断,让 litpic 保持空,模板里再判断;
  2. 不改源码,模板里识别 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:emptyimg[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 升级时会被覆盖。三种保护:

  1. 升级前 git diff 备份,升级后手动 merge;
  2. 把改造放到独立的 hook 文件(DedeBIZ 提供事件 hook 机制,可在不动核心文件的情况下注入逻辑);
  3. 切到 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 loadloading="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。

分享到
标签
版权声明

本文标题:《DedeCMS列表页无缩略图不显示:arc.listview.class.php改造、runphp单双引号陷阱与CSS现代化方案》

本文链接:https://zhangwenbao.com/dedecms-listpage-thumbnail-setting-up-method.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交