织梦DedeCMS手机端文章图片自适应实战:runphp正则去除img宽高样式
保哥从 2014 年开始陆续接手过几十个织梦 DedeCMS 项目,2016 年前后开始大量遇到一个共性问题:PC 端编辑器里粘贴的图片在手机模板上显示不下,要么超出屏幕被横向裁切,要么把整页撑爆出现横向滚动条。原因很简单——百度编辑器、UEditor 在保存富文本时会把图片的 width、height 写死成像素值,比如 <img src="..." width="800" height="600">,PC 看着没问题,到了 375px 宽的手机屏自然就溢出了。
这篇笔记记录保哥目前在生产环境跑了五年多的解决方案:用 {dede:field.body runphp=yes} 配合正则把 img 标签里所有写死的宽高(包括 HTML 属性和内联 style)剥掉,再交给 CSS 接管自适应。文末附 FAQ 总结常见踩坑。
一、问题根源:编辑器写死的宽高
打开 DedeCMS 后台 → 系统 → 系统配置 → 核心设置,看一下 body 字段使用的编辑器。绝大多数老站是 UEditor 或者百度编辑器旧版,它们的默认行为是:
- 上传图片时按图片真实分辨率写入
width/height。 - 用户在编辑器里手动拖动图片缩放时,会把缩放结果写进
style="width:800px;height:600px"。 - 复制其他网站文章过来时,原网站的
style也会一起带过来。
这三种写法在手机模板上都会导致溢出。仅仅靠 CSS img{max-width:100%;} 是搞不定 style="width:800px" 的,因为内联 style 优先级最高,CSS 必须 !important 才能覆盖,而 !important 又会破坏其他需要保持原尺寸的图片(比如表情、二维码图标)。
所以更稳妥的思路是:在输出 HTML 时,把 img 标签里写死的宽高全部抹掉,然后让 CSS 用普通优先级接管。
二、核心方案:runphp 正则替换
打开手机端文章内容页模板(一般在 /templets/default_m/article_article.htm 或者你站点对应的手机模板路径),找到这一行:
{dede:field.body/}替换成下面这段:
{dede:field.body runphp=yes}
global $cfg_basehost;
$str = @me;
$search = '/(<img.*?)width=(["\'])?.*?(?(2)\2|\s)([^>]+>)/is';
$search1 = '/(<img.*?)height=(["\'])?.*?(?(2)\2|\s)([^>]+>)/is';
$search2 = '#(<img.*?style=".*?)width:\d+px;([^"]*?.*?>)#i';
$search3 = '#(<img.*?style=".*?)height:\d+px;([^"]*?.*?>)#i';
$content = preg_replace($search, '$1$3', $str);
$content = preg_replace($search1, '$1$3', $content);
$content = preg_replace($search2, '$1$2', $content);
$content = preg_replace($search3, '$1$2', $content);
@me = $content;
// @me = str_replace('/uploads/allimg/', $cfg_basehost.'/uploads/allimg/', $content); // 可选:手机版图片改用绝对路径
{/dede:field.body}四条正则分别在做这些事:
$search:匹配并去掉<img>里的width="800"这种 HTML 属性。$search1:去掉height="600"这种 HTML 属性。$search2:去掉style里的width:800px;。$search3:去掉style里的height:600px;。
保哥提醒一句:保存模板时一定要用 UTF-8 无 BOM 编码,否则 DedeCMS 解析 runphp 段会出诡异错误。Windows 下可以用 Notepad++ 或 VS Code 切换编码模式后再保存。
三、模板里的 < 转义说明
眼尖的同学会发现,原始示例代码里写的是 <img.*? 而不是 <img.*?。这不是写错了,而是 DedeCMS 模板解析器的“双重转义”机制:
- 模板文件被 DedeCMS 解析器读入时,会把
<>当成标签分界,直接写<img容易让解析器把它当 HTML 标签开始。 - 所以官方推荐在
runphp块里把 HTML 标签字面量写成<、>,DedeCMS 在生成 PHP 代码时会自动还原成<>。
如果你直接复制保哥上面那段“已经转回 < 的版本”放进模板里,多数 DedeCMS 5.7 SP2 也能正常跑,但保险起见,在 5.6 及更老的版本里建议保留 < > 写法:
$search = '/(<img.*?)width=(["\'])?.*?(?(2)\2|\s)([^>]+>)/is';这是织梦官方文档里给出的标准写法,跨版本兼容性最好。
四、CSS 端配合:让图片真正自适应
光把宽高抹掉还不够,得让 CSS 接管尺寸。在手机端的全局样式里加一段:
.article-content img {
max-width: 100%;
height: auto;
display: block;
margin: 12px auto;
border-radius: 6px;
}
.article-content img[src*="qrcode"],
.article-content img.icon {
max-width: none;
width: auto;
display: inline-block;
margin: 0;
}保哥的小习惯:
.article-content用具体类名而不是直接写img,避免误伤导航 logo 之类的小图。- 二维码、表情等小图单独豁免,否则
max-width:100%会让它们跟着容器拉伸变模糊。 height: auto必须加,不然有些浏览器会以 0 计算高度,造成图片塌陷。display: block; margin: auto;让单图水平居中,符合移动端阅读习惯。
五、进阶:图片懒加载与 CDN 路径替换
抹掉宽高之后,可以顺手把懒加载和 CDN 路径替换也一起做了,避免再起一个钩子。在 runphp 块里继续加几行:
// 懒加载:把 src 改成 data-src,再用 JS 滚动到视口时还原
$content = preg_replace(
'/<img(.*?)src=(["\'])(.*?)\2/is',
'<img$1data-src=$2$3$2 src="/images/blank.gif"',
$content
);
// CDN 路径:把站内 /uploads/ 全部替换成 CDN 域名
$cdn = 'https://cdn.example.com';
$content = str_replace('/uploads/allimg/', $cdn.'/uploads/allimg/', $content);前端加一个 IntersectionObserver 即可:
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
const img = e.target;
img.src = img.dataset.src;
io.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('.article-content img[data-src]').forEach((i) => io.observe(i));保哥实测下来,这一套组合拳打完,手机端首屏图片字节数能压到原来的 30%-40%,LCP 在 4G 下基本能稳定在 2 秒内。
六、常见踩坑提醒
保哥列几个最容易翻车的点,照着避就行:
- 正则里的引号要看清是单引号还是双引号。
runphp块本身是单引号字符串,里面再嵌套单引号要用\'转义,复制粘贴时容易丢掉反斜杠。 - 修改完模板要清缓存。DedeCMS 后台 → 生成 → 更新文档 HTML,否则前端看不到变化。
- 如果用了静态生成,记得整站重新生成一次 HTML,否则只有新发布的文章会用新模板。
- PC 端别也套这套正则。PC 模板上图片往往依赖原尺寸做版式,强行抹宽高会把版心搞乱。这套方案只在手机模板里用就够了。
- 图集模型
addonimages不走 body 字段,所以这套方案不影响图集页,图集页另有arclistsg等标签。
常见问题 FAQ
Q1:用了 runphp=yes 之后页面 500 怎么办?
先检查模板文件编码,必须 UTF-8 无 BOM。然后到 /data/tplcache/ 把对应缓存文件删掉,让 DedeCMS 重新解析模板。如果还是 500,打开 /include/common.inc.php 顶部把错误显示打开看具体行号,多半是引号或转义出问题。
Q2:替换之后图片虽然不溢出了,但宽高比变形了怎么办?
检查 CSS 有没有写 height: auto,缺这一句浏览器就不会按比例缩放。还有一种情况是父容器有 display: flex + align-items: stretch,图片会被拉成容器高度,把对齐方式改成 flex-start 即可。
Q3:为什么有些文章图片还是溢出?
大概率是文章里用了 <table> 嵌套图片,CSS 选择器只覆盖了 .article-content img 没覆盖到表格里。补一条 .article-content table { max-width: 100%; } 一般就好了。还有一种情况是图片用了 <figure> 包裹,记得给 figure 也加 max-width: 100%。
Q4:我想让 PC 端保留宽高、只在手机端抹掉,可以吗?
完全可以,这套 runphp 方案本来就只放在手机模板里。如果你的手机端是和 PC 端共用模板靠 CSS 媒体查询区分的,可以改成在 PHP 里判断 User-Agent:检测到移动设备再执行正则替换,PC 直接 @me = $str; 跳过。逻辑大约 5 行代码就能写完。
以上就是保哥这套手机端图片自适应方案的全部细节,从根因分析到正则、CSS、懒加载、CDN、再到常见坑位,覆盖了五年多生产环境验证下来的所有关键点,希望对还在用 DedeCMS 维护移动端的同学有所帮助。