给案例图补上 alt 并不难,难的是让说明始终对应图片:原图和结果图说的是不是同一件商品,描述的是处理目标还是实际画面,图片加载失败后换了备用素材,文字有没有一起换?这些问题比"每张图都有 alt"更接近工具页的真实维护成本。
本文以我维护的图片猫(PicCat)为例,项目入口为 www.piccat.cn。我沿商品换色工具页的案例配置和公共组件检查了一遍,用仓库中的运动鞋、沙发素材说明 alt 的写法,以及实现中仍需处理的边界。
本文讨论网页 img 元素的替代文本。示例图片来自项目已有案例素材,仅重新并排排版;它们不是本次调用模型生成的结果,也不能代表工具对任意输入的处理效果。
1 先问图片在当前页面承担什么作用
商品换色页的主案例图承载具体信息:换的是哪个对象、原来是什么颜色、结果变成什么样。仅写"处理前""处理后",读者仍然不知道画面内容。写成一串"AI 换色、免费在线换色、商品图处理",也无法补足这些信息。
我会先看图片所在的位置。主对比图需要表达画面状态;用于切换案例的缩略图,可能只是旁边案例标题的视觉补充;只有图标、没有文字的按钮,则需要说明操作目的。W3C WAI 的图片教程也按图片用途区分替代文本,不能只按文件类型决定写法。12
|------------|---------------------------|------------------|
| 所在位置 | 优先表达的信息 | 需要避免 |
| 处理前后主图 | 主体、当前状态、与本次操作相关的变化 | 两张图都只写"效果图" |
| 带文字的案例切换按钮 | 由按钮文字说明案例;图片若无额外信息可用空 alt | 图片说明与可见标题重复朗读 |
| 只有图片的操作入口 | 操作或目的,例如"放大查看运动鞋换色结果" | 只有"运动鞋",却不说明入口作用 |
| 纯装饰图片 | 明确写 alt="" | 省略属性后让文件名承担说明 |
空 alt 与缺少 alt 不是同一件事。是否允许空值,要结合周围文本和交互名称判断。主案例图如果承担独立的对比信息,通常应保留有效说明;缩略图是否冗余,则要检查它所在的整个按钮。
2 把场景说明放进案例数据
当前商品换色页 EcommerceProductRecolor.vue 在上传区的 showcase-sections 插槽中,把 ecommerceProductRecolorFixedCases 传给 FixedShowcaseStorySections。后者再把 beforeAlt、afterAlt 原样传入 BeforeAfterSlider,由滑块绑定到两张 img 上。
这条链路把"知道画面内容的配置"和"负责显示的组件"分开。通用滑块不需要猜运动鞋是什么颜色,也不需要根据文件名生成商品说明。ShowcaseCase 类型已经提供 beforeAlt、afterAlt、thumbnailAlt;不过它们都是可选字段,类型本身并不保证每个案例都写了有效说明。
项目代码摘录|运动鞋案例的说明字段
beforeLabel: '纯白运动鞋',
afterLabel: '亮橙色鞋面',
beforeAlt: 'AI 商品换色案例:白色运动鞋商品图',
afterAlt: 'AI 商品换色案例:运动鞋改为亮橙色后保留网面和鞋带细节',
片段来自 ecommerceProductRecolorFixedCases.ts,省略了案例 ID、图片路径、标题和 caption。beforeLabel、afterLabel 用作图片上的短标签;alt 提供图片的文字替代;caption 则放在图片附近,解释整个案例。三个字段用途不同,不必机械地复制同一段内容。
项目代码摘录|FixedShowcaseStorySections.vue 中的属性传递
<BeforeAfterSlider
:before-src="item.before"
:after-src="item.after"
:before-label="item.beforeLabel"
:after-label="item.afterLabel"
:before-alt="item.beforeAlt"
:after-alt="item.afterAlt"
:aspect-ratio="item.previewAspectRatio"
:tool-name="toolName"
:image-loading="index === 0 ? 'eager' : 'lazy'"
/>
这是循环内部的真实片段,省略了外层 article 和文字区域,不能独立运行。新增案例时,维护一条完整的数据记录即可复用这条传递链;替换图片时,也应把同条记录中的标签、alt 和 caption 一起复核。
3 从实际画面写出处理前后的差异

运动鞋案例很适合说明"主体+状态"的写法。原图可以写"白色运动鞋侧面展示,鞋面为网布与拼接结构";结果图可以写"运动鞋由白色改为橙色,网面和鞋带仍可见"。这些是建议文案,不是当前配置的逐字摘录。
如果页面标题已经写清"AI 商品换色",alt 可以直接从运动鞋说起,不必每张都重复工具名。颜色变化是这组图的重点,背景墙的细节就不必逐项描述。另一方面,画面里能看见网面,不等于已经证明处理"无损",所以我倾向于用"仍可见"而不是"完美保留"。
这套写法可以按处理类型调整:抠图说明主体及背景变化,消除说明被移除的对象和留下的区域,改字说明与任务有关的原文字和新文字。不要把整段提示词直接塞进 alt,也不要补写图片没有展示的处理步骤或质量结论。

沙发案例暴露出另一类问题。当前 afterAlt 写的是"AI 商品换色案例:沙发改为森林绿后客厅背景与光影保持不变"。但直接对照仓库中的前后图片,可以看到左侧边几上的花瓶与枝叶状摆件也变成了绿色。仅凭这组素材,无法支持"背景与光影保持不变"的断言。
更贴近画面的建议写法是:"客厅中的单人沙发改为绿色,左侧摆件也出现绿色变化。"如果产品希望展示的是只修改沙发的案例,应先更换合适的素材,再同步修订说明。靠 alt 把副作用写没了,读者实际看到的矛盾仍然存在。当前仓库的这一处文案尚未在本文中修改。
这里的判断只针对这对本地素材,不推断线上模型的普遍表现。实际图片优先于处理要求:提示词写了"其他区域不变",并不能成为结果说明里的事实依据。
4 默认文案只能兜底 空值必须有语义
BeforeAfterSlider 当前优先使用显式 alt,未提供时拼接工具名和前后标签。这个默认值可以避免完全没有说明,但不包含具体主体,无法替代逐案例文案。
项目代码摘录|BeforeAfterSlider.vue 的前图 alt 计算,调整换行
const computedBeforeAlt = computed(() => (
props.beforeAlt || (
props.toolName
? `{props.toolName} - {props.beforeLabel}`
: props.beforeLabel
)
));
后图采用相同逻辑。这里使用 ||,会把空字符串也视为"未填写":调用方传 beforeAlt="",得到的仍是"AI 商品换色 - 处理前"。反过来,只有空格的字符串属于真值,会绕过默认值。是否有 alt 属性与是否有有效描述,需要分开检查。
对承载信息的主图,最简单的做法是在案例数据校验中拒绝空白说明。若公共组件确实需要支持"明确不提供替代文本"的用途,才考虑把未提供与显式空值区分开。下面是后者的建议局部实现,尚未接入项目。
建议改进代码|仅示意前图的空值策略,依赖现有 props 与 computed
const computedBeforeAlt = computed(() => {
const fallback = props.toolName
? `{props.toolName} - {props.beforeLabel}`
: props.beforeLabel;
if (props.beforeAlt === undefined) return fallback;
return props.beforeAlt.trim();
});
这段逻辑约定 undefined 表示使用默认值,显式传入的空白文本会规范为 ""。它不会替你决定图片是否属于装饰内容;主案例配置仍需要禁止误传空值。如果数据来自接口,还应先校验类型,不把 TypeScript 的 string 声明当成运行时保证。
5 切换图片和加载回退也要切换说明
另一条案例展示路径 FeatureShowcaseGallery.vue 使用 activeCase 同时提供主图地址和 alt。发生主图加载错误且有备用案例时,组件会切换到 fallbackCases,主图资源和说明因而取自同一条备用记录。这与商品换色页的固定长图文案例入口不同,不应混为同一条渲染路径。
但缩略图回退是单独处理的。getMatchingFallbackCase 优先按 ID 匹配,找不到时使用相同下标的备用案例;getThumbnailSrc 切换图片来源,模板中的 alt 仍读取原来的 item。下面只摘录与问题相关的两处绑定。
项目代码摘录|FeatureShowcaseGallery.vue 的缩略图绑定,省略其他属性
<img
:src="getThumbnailSrc(item, index)"
:alt="item.thumbnailAlt || `${item.title}处理效果`"
/>
当两套案例恰好同序、内容一致时,这种写法未必出错;如果某个缩略图失效,而同下标备用项对应另一件商品,就可能出现"图片是沙发,说明仍是运动鞋"。这是现有逻辑在特定数据条件下的风险,不是已经确认的线上故障。
建议先确定备用关系,再统一解析当前缩略图的资源和元数据。无法确认是同一案例时,保留原案例标题并显示中性占位,通常比换成另一件商品更准确。仅把备用 alt 换过来也不够,因为点击后打开的仍可能是原案例,图片、说明和操作目标必须一起对应。
缩略图按钮里还已有可见的 item.title。如果缩略图只是这段标题的视觉补充,可以让图片使用 alt="",由按钮文字提供名称;如果图片传递了标题没有的信息,再保留简短补充。当前模板使用 ||,同样不能直接通过 thumbnailAlt="" 表达前一种意图。WAI 的功能图片示例说明了有文字的链接如何避免重复,而只有图片的入口则必须有可用名称。2
6 验证文本之外 还要验证实际语义
本次做了源码局部执行和素材核对:5 条固定换色案例都有非空的前后 alt,对应的 10 个本地图片文件均存在;从滑块源码提取计算逻辑后,确认显式文本被保留、空字符串触发默认文案、纯空格不会触发默认文案。
另外,用"原案例是运动鞋、同下标备用案例是沙发"的构造数据执行缩略图来源函数,确认它可返回沙发图片地址,而原模板表达式仍返回运动鞋说明。这个验证只证明局部逻辑可以产生错配,不代表实际接口正在返回这组数据,也没有覆盖完整页面、网络和读屏软件。
还有一个容易被 alt 检查遗漏的结构问题:当前 BeforeAfterSlider 把 role="slider" 放在包住两张图片的根元素上,并设置固定 aria-label"处理前后对比滑块"。MDN 说明,slider 的后代元素语义会被呈现性处理。因此,即使 DOM 中两张图都有 alt,也不能据此认定辅助技术会把它们作为两个独立图片读出。3
建议把对比内容与滑块控制分开组织,或在控件外提供明确关联的案例说明,并结合实际无障碍树核验。这里不能只把根节点的 aria-label 改长就宣布完成:控制名称、当前数值的含义、前后画面的说明和键盘操作都需要一起验证。本文没有对该结构实施改造或完成读屏验收。
|-----------|-----------------------------|
| 建议验收项 | 应检查的结果 |
| 逐案例看图读文 | 主体、颜色、前后状态准确;副作用没有被质量承诺掩盖 |
| 切换案例与接口更新 | src、alt、caption 和点击目标属于同一案例 |
| 模拟图片加载失败 | 备用素材语义一致;无法匹配时不使用另一案例冒充 |
| 空值与重复信息 | 区分省略、空字符串和空白文本;按钮名称不过度重复 |
| 辅助技术与键盘 | 检查无障碍树及读屏输出;滑块名称、数值、说明可理解 |
这些是建议执行的验收项,不是本次已全部通过的测试。自动检查适合发现属性缺失、空白文本和资源路径问题;"森林绿""背景不变"是否符合图像,仍需要人工对照素材。也不要用一个固定字符数上限代替信息质量判断。
7 让 alt 随案例一起维护
对工具页而言,好的 alt 来自可复核的案例记录:描述谁、呈现什么状态、这次变化是什么;图片更新后,同步检查说明和交互。通用组件保留合理兜底,但不替业务猜场景;备用图也必须遵守同一套对应关系。
Google 的图片指南建议让 alt 与页面上下文相关、提供有用信息,并避免关键词堆砌。4 这支持我们把场景写清楚,但不意味着补完 alt 就能保证收录或排名。本文没有进行流量对照实验,也不把文案修订当成 SEO 效果证明。
对我来说,这次核对最有价值的发现是沙发案例:字段存在、组件传值正确,文字依然可能与画面不符。真正需要持续维护的是图片与说明的对应关系。
源码位置与参考资料
项目代码均位于 webClientVue/src:components/ecommerce/EcommerceProductRecolor.vue 为页面接入;config/ecommerceProductRecolorFixedCases.ts 为本文两组案例的数据来源;config/showcasePairs.ts 定义案例类型;components/common 下的 FixedShowcaseStorySections.vue、BeforeAfterSlider.vue 和 FeatureShowcaseGallery.vue 分别承担传值、对比显示与画廊回退。
两组图片来自 webClientVue/public/showcase/cases/ecommerce-product-recolor 下的 sneaker-color-before/after.webp 与 sofa-color-before/after.webp。源码结论针对本次检查的本地版本,建议代码与当前实现已分别标明。