1. 项目概述:为什么从“小米商城网页”切入HTML实战?
做前端开发的同行应该都经历过这个阶段:学完HTML标签、CSS选择器、基础盒模型,打开编辑器却不知道该写什么。不是不会,而是缺乏一个真实、完整、有业务逻辑的载体来串联所有知识点。我带过不少刚入门的学员,他们能背出<div>和<span>的区别,但一让做一个商品列表页,就卡在结构怎么嵌套、语义怎么组织、响应式怎么起步——这恰恰说明,脱离真实场景的碎片化学习,就像只学了螺丝刀和扳手,却没见过整台发动机。
“Html——小米商城网页实战(一)”这个标题,表面看是模仿一个电商页面,实则是一套经过验证的HTML能力训练路径。它不追求像素级还原,也不堆砌炫酷动效,而是聚焦于结构合理性、语义准确性、可访问性基础、以及与后续CSS/JS衔接的扩展性。比如,你可能觉得一个轮播图区域用<div class="banner">就够了,但在真实项目中,它必须是<section aria-label="首页主推活动轮播">,里面每个图片要有alt描述,焦点顺序要可键盘导航——这些细节,教材里很少讲,但上线前测试一定会被挑出来。
这个实战特别适合三类人:一是刚学完HTML基础、想立刻动手验证理解的新手;二是自学多年但总感觉“会写但写不好”的过渡期开发者;三是需要给团队新人设计培训材料的技术负责人。它解决的不是“能不能显示”,而是“为什么这样组织更健壮”“改起来是否容易”“盲人用户能否顺畅使用”这类工程化问题。我试过把这套结构直接用在某高校实验室的课程设计中,学生反馈最深的一点是:“原来<nav>不只是为了加个背景色,而是告诉屏幕阅读器‘这是导航区’”。
关键词“小米商城”在这里是典型样本,不是品牌合作,也不是商业复刻。我们取它的典型模块:顶部通栏、主导航、搜索框、轮播图、分类入口、商品瀑布流、底部信息。每个模块都对应HTML核心能力点:通栏考验全局结构(<header>+<nav>嵌套)、搜索框涉及表单语义(<form>+<label>+<input>组合)、商品列表直指语义化列表(<ul>+<li>+<article>)。这些不是凭空设计的,而是基于W3C HTML5.2规范中对“文档大纲算法”的要求,确保生成的DOM树天然具备逻辑层级。
2. 整体结构设计与思路拆解:从“画布”到“骨架”
2.1 为什么放弃“先写CSS再套HTML”的惯性思维?
很多初学者一上来就想着“怎么让这个导航栏变蓝”“怎么让轮播图自动切换”,结果HTML结构写得松散混乱:导航用一堆<div>硬撑,商品列表用<p>包裹图片,甚至用<br>强行换行。等后期加CSS时,发现样式冲突严重,改一个地方全乱套。我带过的某位A同学就踩过这个坑——他花三天做出一个“看起来像”的首页,但当我让他把导航菜单改成下拉式时,他花了两天重写结构,因为原代码里根本没有清晰的菜单层级概念。
所以本实战的第一原则是:HTML先行,语义驱动。我们先把整个页面当成一份纯文本文档来规划,不考虑颜色、间距、动画,只回答一个问题:“如果关闭所有CSS,这个页面的信息结构是否依然清晰、可读、可导航?” 这就是“无样式优先”(Style-agnostic First)的设计思路。它直接对应HTML5的语义化标签体系,比如:
<header>:定义页面或区块的头部,不是“放logo的地方”,而是“承载主要介绍性内容的容器”;<nav>:专用于导航链接集合,浏览器和辅助技术会识别为独立导航区域;<main>:页面核心内容,一个文档只能有一个,且不能嵌套在<article>或<aside>内;<section>:具有主题的独立内容块,比如“热门推荐”“新品首发”,必须有明确标题(<h2>或更高);<article>:可独立分发的内容单元,如单个商品卡片,它自带隐含的语义边界。
这种结构不是教条,而是工程实践沉淀下来的避坑指南。某公司曾因首页<nav>标签缺失,导致其网站在搜索引擎结果页中导航链接无法聚合展示,SEO流量下降17%。这不是玄学,是标准落地的直接反馈。
2.2 页面骨架的三层嵌套逻辑
我们把整个页面拆解为三个逻辑层,每层解决一类问题:
第一层:文档主体框架(Document Shell)
这是HTML的“地基”,决定整个页面的宏观结构。它由<html>根元素开始,内部严格按顺序包含:
<head>:存放元数据(字符集、视口设置、页面标题),绝不放任何可见内容;<body>:所有可见内容的父容器,内部仅允许出现<header>、<nav>、<main>、<aside>、<footer>等语义化区块标签。
提示:
<body>内禁止直接写<div>或<p>作为顶级元素。我见过太多代码以<div class="wrap">开头,这等于把整个页面塞进一个无意义的盒子,破坏了文档大纲。
第二层:功能区块划分(Functional Zones)
在<body>下,我们按用户视线流和功能职责划分五大区块:
<header>:顶部通栏(Logo、登录/注册、购物车图标);<nav>:主导航菜单(手机、电视、笔记本等一级类目);<main>:页面主体(轮播图、分类入口、商品列表);<aside>:侧边栏(可选,如“客服在线”“今日推荐”);<footer>:底部信息(版权、友情链接、备案号)。
关键点在于:<nav>必须是<header>的子元素还是独立兄弟元素?答案是——取决于导航的语义范围。如果导航只服务于顶部通栏(如“登录”“购物车”),它属于<header>内部;如果导航是全站主导航(如“小米手机”“Redmi”),它必须是<body>的直接子元素,与<header>并列。本实战采用后者,因为主导航是全站核心路径。
第三层:内容单元组织(Content Units)
这是最易出错的层级。比如商品列表,新手常写成:
<div class="product-list"> <div class="product-item">...</div> <div class="product-item">...</div> </div>这完全丢失了语义。正确写法是:
<section aria-labelledby="product-title"> <h2 id="product-title">精选好物</h2> <ul class="product-grid"> <li class="product-item"> <article> <h3>小米14 Pro</h3> <img src="..." alt="小米14 Pro 5G智能手机,黑色,16GB+512GB"> <p>¥4999</p> </article> </li> </ul> </section>这里<ul>表示“一组并列的商品”,<li>是列表项,<article>强调每个商品是独立可分发的内容单元。aria-labelledby将标题与列表关联,让屏幕阅读器能准确播报“精选好物”下的内容。这种写法,后期加CSS网格布局(display: grid)或JS动态加载时,结构依然稳固。
2.3 响应式结构的前置设计
很多人以为响应式是CSS媒体查询的事,其实HTML结构决定了响应式能否优雅实现。比如顶部通栏,在移动端需要折叠为汉堡菜单。如果原始结构是:
<div class="top-bar"> <div class="logo">小米</div> <div class="menu-items">手机 电视 笔记本</div> <div class="user-actions">登录 购物车</div> </div>那么JS控制显示/隐藏时,你得操作三个不同<div>,逻辑复杂且易出错。而语义化结构应为:
<header> <a href="/" class="logo">小米</a> <nav class="main-nav" aria-label="主导航"> <ul> <li><a href="/phone">手机</a></li> <li><a href="/tv">电视</a></li> <li><a href="/laptop">笔记本</a></li> </ul> </nav> <div class="user-tools"> <a href="/login">登录</a> <a href="/cart" aria-label="购物车,当前有3件商品">🛒</a> </div> </header>这样,JS只需控制.main-nav的display属性,或添加hidden属性,结构清晰,无副作用。aria-label为购物车图标提供无障碍说明,<a>标签保证键盘焦点可达——这些都不是“锦上添花”,而是现代Web开发的基础设施。
3. 核心细节解析与实操要点:标签选择、属性规范与避坑清单
3.1 关键标签的不可替代性:为什么不用<div>代替一切?
<div>是通用容器,但它没有语义。在HTML5中,超过80%的常见页面区域都有专用语义标签。强行用<div>替代,短期看省事,长期看埋雷。以下是本实战中必须严格使用的标签及其不可替代理由:
<nav>vs<div class="nav"><nav>被浏览器和辅助技术识别为“导航区域”,屏幕阅读器用户可通过快捷键(如NVDA的N键)直接跳转到所有<nav>。而<div class="nav">只是普通容器,需额外加role="navigation"才能模拟,且不保证兼容性。某次无障碍审计中,某电商站因用<div>替代<nav>,导致视障用户无法快速定位主导航,整改耗时一周。
<main>vs<div id="main"><main>是文档的唯一主要内容区,浏览器会为其分配main角色。若页面存在多个<main>,或<main>嵌套在<article>内,会导致文档大纲混乱,影响SEO和可访问性。W3C明确要求:一个页面只能有一个<main>,且不应是<article>、<aside>、<footer>、<header>或<nav>的后代。
<article>vs<div class="card"><article>表示“可独立分发、可复用的内容”,如一篇博客、一条新闻、一个商品。它自带隐含的article角色,且其内部<h1>-<h6>会形成新的大纲层级。用<div>则完全丢失此能力。商品卡片用<article>,意味着未来可直接将其提取为RSS源或独立API返回项。
<figure>+<figcaption>vs<div>+<p>
轮播图中的每张图片,若带有说明文字(如“小米SU7 高性能纯电轿车”),必须用<figure>包裹图片,<figcaption>放置说明。<figure>表示“自包含的流内容”,<figcaption>是其标题,两者语义绑定。这比<div><img><p>组合更能表达“图与文一体”的关系,且对打印样式、语音合成更友好。
3.2 属性使用的黄金法则:哪些必须加,哪些可以省
HTML属性不是越多越好,而是“该出现时必须出现”。以下是本实战中强制要求的属性清单及原理:
alt属性:图片的“文字身份证”
所有<img>标签必须有alt属性。规则很简单:
- 图片是内容(如商品主图):
alt="小米14 Pro 5G智能手机,黑色,16GB+512GB"(描述图片本身,含关键参数); - 图片是装饰(如分割线、背景花纹):
alt=""(空字符串,明确告知“无信息”); - 图片是功能(如搜索按钮图标):
alt="搜索"(描述功能,非外观)。
注意:
alt="小米手机图片"是典型错误,它没描述图片内容,只说了“这是图片”。我检查过上百份学员作业,超60%的alt写法不合格,根源是对“替代文本”本质理解偏差——它不是给开发者看的备注,而是给无法看到图片的用户提供的等效信息。
aria-*属性:补足语义缺口的“胶水”
ARIA(Accessible Rich Internet Applications)不是用来替代语义标签的,而是当原生标签无法满足需求时的补充。本实战中必须使用的有:
aria-label:为无文本的控件提供标签,如购物车图标<a href="/cart" aria-label="购物车,当前有3件商品">🛒</a>;aria-labelledby:将元素与页面中已有的标题关联,如轮播图区域<section aria-labelledby="carousel-title">,配合<h2 id="carousel-title">首页轮播</h2>;aria-hidden="true":隐藏纯装饰性内容(如图标字体<i class="icon-home" aria-hidden="true"></i>),防止屏幕阅读器误读。
lang属性:语言标识的“护照”<html lang="zh-CN">必须声明。它告诉浏览器和辅助技术,页面主要内容是简体中文。这影响拼写检查、语音合成的发音、字典匹配等。漏掉它,可能导致屏幕阅读器用英文发音读中文,体验极差。
type属性:表单控件的“类型说明书”
搜索框<input>必须写type="search"而非type="text"。search类型会触发浏览器特定行为:iOS Safari显示“取消”按钮,Chrome支持搜索历史,且语义上明确这是搜索功能。同理,邮箱输入用type="email",数字输入用type="number>,它们不仅美化UI,更提供原生校验和软键盘优化。
3.3 常见结构陷阱与现场修正方案
在实际教学中,我发现以下五类结构错误出现频率最高,附带即时修正方法:
陷阱1:标题层级断裂(H1-H6跳跃或缺失)
错误示例:页面无<h1>,直接从<h2>开始;或<section>内用<h3>但没<h2>。
后果:文档大纲断裂,SEO权重分散,屏幕阅读器无法构建逻辑目录。
修正方案:全站统一<h1>为Logo或品牌名(<h1><a href="/">小米</a></h1>),每个<section>必须有且仅有一个<h2>作为该区块标题,<article>内用<h3>表示商品名。用 HeadingMap 插件实时检查大纲。
陷阱2:列表滥用与误用
错误示例:用<ul>做导航菜单但<li>内无<a>;或用<p>包裹多个商品信息。
后果:导航链接不可键盘聚焦,商品信息无法被列表快捷键遍历。
修正方案:导航菜单必须是<nav><ul><li><a>...</a></li></ul></nav>;商品列表必须是<section><h2>...</h2><ul class="grid">...<li><article>...</article></li>...</ul></section>。记住:<ul>/<ol>只用于“并列项”,<article>用于“独立内容”。
陷阱3:表单标签缺失
错误示例:搜索框只有<input type="search" placeholder="搜索小米产品">,无<label>。
后果:屏幕阅读器无法告知用户“这是搜索框”,触控设备点击区域小。
修正方案:必须用<label for="search-input">搜索</label><input type="search" id="search-input">,或包裹式<label>搜索<input type="search"></label>。for与id匹配是关键。
陷阱4:空链接与伪按钮
错误示例:<a href="#" onclick="toggleMenu()">☰</a>或<div onclick="submitForm()">提交</div>。
后果:键盘用户无法聚焦,屏幕阅读器读作“链接”但无目标,SEO忽略。
修正方案:导航按钮用<button type="button" aria-expanded="false" aria-controls="main-nav">☰</button>;表单提交用<button type="submit">提交</button>。<button>天生可聚焦、可键盘操作、语义明确。
陷阱5:时间与数字的机器可读标记
错误示例:<p>上市时间:2023年12月</p>或<p>价格:¥4999</p>。
后果:无法被搜索引擎提取为结构化数据,影响富摘要展示。
修正方案:用<time datetime="2023-12">2023年12月</time>和<span itemprop="price" content="4999">¥4999</span>(配合itemscope和itemtype)。虽非本实战重点,但提前植入意识。
4. 实操过程与核心环节实现:从零搭建首页HTML骨架
4.1 环境准备与文件初始化
无需复杂工具,一个文本编辑器(VS Code推荐)和现代浏览器(Chrome/Firefox)足矣。创建项目文件夹xiaomi-mall-html,内部新建:
index.html:主页面文件;css/文件夹:存放CSS(本实战暂不写,但预留结构);images/文件夹:存放占位图(可用 placehold.co 生成,如https://placehold.co/300x200/409EFF/FFFFFF?text=小米14+Pro)。
index.html初始模板必须包含HTML5标准结构:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>小米商城 - 为发烧而生</title> <meta name="description" content="小米官方商城,提供小米手机、电视、笔记本、智能家居等全品类产品在线购买服务。"> </head> <body> <!-- 页面内容将在此处编写 --> </body> </html>注意:
<meta name="viewport">是响应式的基石,缺它移动端页面会以桌面宽度渲染,文字小到无法阅读。<title>和<meta description>是SEO第一道门,必须准确反映页面核心。
4.2 顶部通栏(Header)的逐行实现
顶部通栏是用户第一眼看到的部分,需兼顾品牌传达与功能入口。我们按视觉从左到右、逻辑从外到内编写:
<header> <!-- 左侧Logo --> <h1 class="logo"> <a href="/" title="返回首页">小米</a> </h1> <!-- 中部主导航(注意:此处是全站主导航,非通栏内导航) --> <nav class="main-nav" aria-label="主导航"> <ul> <li><a href="/phone" aria-current="page">手机</a></li> <li><a href="/tv">电视</a></li> <li><a href="/laptop">笔记本</a></li> <li><a href="/home">智能家居</a></li> <li><a href="/wearable">穿戴设备</a></li> <li><a href="/router">路由器</a></li> <li><a href="/service">服务</a></li> <li><a href="/about">关于小米</a></li> </ul> </nav> <!-- 右侧用户工具 --> <div class="user-tools"> <a href="/login" aria-label="用户登录">登录</a> <a href="/register" aria-label="用户注册">注册</a> <a href="/cart" aria-label="购物车,当前有0件商品"> <span class="cart-icon">🛒</span> <span class="cart-count">0</span> </a> </div> </header>关键细节说明:
<h1>包裹Logo链接,既满足主标题要求,又提供可点击入口。title属性为链接提供额外说明。- 主导航
<nav>使用aria-label明确其作用,每个<a>链接用aria-current="page"标注当前激活项(本例中“手机”页),方便屏幕阅读器识别。 - 购物车链接内部分离图标与计数,
<span class="cart-icon">用aria-hidden="true"隐藏图标(因图标本身无信息),<span class="cart-count">用aria-live="polite"(后续JS更新时)通知变化。 - 所有链接
href使用相对路径,符合静态页面部署习惯。
4.3 搜索区域(Search Section)的语义化构建
搜索是电商核心功能,其HTML结构直接影响可用性。避免写成<div class="search-box"><input><button>搜索</button></div>,而应:
<section class="search-section" aria-labelledby="search-title"> <h2 id="search-title" class="visually-hidden">站内搜索</h2> <form action="/search" method="GET" role="search"> <label for="site-search" class="visually-hidden">搜索小米产品</label> <input type="search" id="site-search" name="q" placeholder="搜索小米产品,例如:小米14、米家扫地机器人" autocomplete="off" required > <button type="submit" aria-label="搜索"> <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"> <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/> </svg> </button> </form> </section>为什么这样写?
<section>包裹整个搜索区域,aria-labelledby关联隐藏标题,确保语义完整。<h2>用class="visually-hidden"隐藏(CSS设为position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;),对视觉用户不可见,但对屏幕阅读器可见,解决“无标题”问题。<form>必须有action和method,role="search"显式声明其为搜索表单,浏览器可优化行为(如Safari在地址栏显示搜索图标)。<label>同样隐藏,但为<input>提供明确关联,for与id匹配。placeholder是补充提示,不能替代<label>。<button type="submit">是表单提交的标准方式,aria-label为图标按钮提供无障碍文本。SVG图标用aria-hidden="true"隐藏,避免重复播报。
4.4 轮播图区域(Carousel)的结构化实现
轮播图常被误认为“纯视觉组件”,实则需最强语义保障。我们不写JS逻辑,只搭HTML骨架:
<section class="carousel" aria-labelledby="carousel-title"> <h2 id="carousel-title">首页轮播</h2> <div class="carousel-container" role="region" aria-live="polite" aria-label="轮播图区域"> <div class="carousel-track" aria-roledescription="carousel"> <!-- 第一张幻灯片 --> <figure class="carousel-slide" aria-hidden="false"> <img src="https://placehold.co/1200x400/FF6B6B/FFFFFF?text=小米SU7+发布" alt="小米SU7 高性能纯电轿车,0-100km/h加速2.78秒,续航800km" loading="eager" > <figcaption class="slide-caption"> 小米SU7 颠覆性能新标杆 </figcaption> </figure> <!-- 第二张幻灯片 --> <figure class="carousel-slide" aria-hidden="true"> <img src="https://placehold.co/1200x400/4ECDC4/FFFFFF?text=小米14+系列" alt="小米14 Pro 5G智能手机,徕卡光学镜头,16GB+512GB" loading="lazy" > <figcaption class="slide-caption"> 小米14 Pro 影像旗舰 </figcaption> </figure> <!-- 第三张幻灯片 --> <figure class="carousel-slide" aria-hidden="true"> <img src="https://placehold.co/1200x400/45B7D1/FFFFFF?text=米家智能生态" alt="米家智能生态,全屋智能联动,小爱同学语音控制" loading="lazy" > <figcaption class="slide-caption"> 米家智能生态 全屋互联 </figcaption> </figure> </div> <!-- 轮播控制 --> <div class="carousel-controls" aria-label="轮播图控制"> <button type="button" aria-label="上一张" class="carousel-btn prev-btn">‹</button> <div class="carousel-indicators"> <button type="button" aria-label="查看第1张幻灯片" aria-pressed="true"><!-- 分类入口 --> <section class="category-section" aria-labelledby="category-title"> <h2 id="category-title">分类导航</h2> <ul class="category-grid"> <li class="category-item"> <a href="/phone/accessories"> <img src="https://placehold.co/120x120/96CEB4/FFFFFF?text=手机配件" alt="手机配件:保护壳、充电器、数据线、耳机" loading="lazy" > <span>手机配件</span> </a> </li> <li class="category-item"> <a href="/home/appliances"> <img src="https://placehold.co/120x120/FFEAA7/FFFFFF?text=大家电" alt="大家电:电视、空调、冰箱、洗衣机" loading="lazy" > <span>大家电</span> </a> </li> <!-- 更多分类... --> </ul> </section> <!-- 商品瀑布流 --> <section class="product-section" aria-labelledby="product-title"> <h2 id="product-title">热销单品</h2> <ul class="product-grid"> <li class="product-item"> <article itemscope itemtype="https://schema.org/Product"> <a href="/product/xiaomi-14-pro"> <img src="https://placehold.co/200x200/409EFF/FFFFFF?text=小米14+Pro" alt="小米14 Pro 5G智能手机,黑色,16GB+512GB,徕卡光学镜头" loading="lazy" itemprop="image" > <h3 itemprop="name">小米14 Pro</h3> </a> <p class="price" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> <span itemprop="priceCurrency">¥</span> <span itemprop="price">4999</span> </p> <p class="desc">徕卡光学镜头,2K AMOLED屏幕,骁龙8 Gen3</p> </article> </li> <!-- 更多商品... --> </ul> </section>关键点:
- 分类入口用
<ul>+<li>+<a>,确保键盘可导航,<img>的alt描述该分类下的典型商品。 - 商品列表用
<article>包裹,加入Schema.org结构化数据(itemscope+itemtype),为搜索引擎提供丰富摘要基础。 itemprop属性将商品名、价格、图片等标记为结构化数据字段,loading="lazy"延迟加载非首屏图片,提升性能。- 所有链接
href指向真实产品路径,保持URL语义化。
5. 常见问题与排查技巧实录:从课堂到项目的实战反馈
5.1 “为什么我的页面在手机上显示很小?”——视口与缩放问题
现象:在手机浏览器打开index.html,页面整体缩小,需双指放大才能阅读。
原因:缺失<meta name="viewport">标签,或content值错误。
排查步骤:
- 检查
<head>中是否存在<meta name="viewport" content="width=device-width, initial-scale=1.0">; - 若存在,确认
content值是否被误写为width=1200或initial-scale=0.5; - 在Chrome DevTools中,切换到手机模式(Ctrl+Shift+M),查看Elements面板中
<meta viewport>是否生效(右上角显示“Viewport: device-width”)。
解决方案:严格使用标准值。width=device-width让页面宽度匹配设备屏幕,initial-scale=1.0禁用初始缩放。切勿添加user-scalable=no,这违反WCAG可访问性准则。
5.2 “屏幕阅读器读不出我的导航菜单!”——ARIA与语义标签缺失
现象:使用NVDA或VoiceOver测试,导航链接被读作“链接”而非“手机链接”“电视链接”。
原因:aria-label缺失,或<nav>未正确包裹链接。
排查步骤:
- 用浏览器开发者工具检查
<nav>是否包含所有导航<a>,且无多余<div>隔离; - 检查每个
<a>是否有aria-label,或其内部文本是否足够描述(如<a href="/phone">手机</a>本身已足够,无需额外aria-label); - 运行 WAVE 无障碍评估工具,查看“ARIA”和“Navigation”报错。
解决方案:移除冗余<div>,确保<nav>直系子元素为<ul>,<ul>子元素为<li>,<li>子元素为<a>。若链接文本不明确(如图标链接),必须加aria-label。
5.3 “商品图片alt文字被搜索引擎判定为垃圾内容”——alt描述质量不达标
现象:Google Search Console提示“图片alt文本过于通用”,影响图片搜索排名。
原因:alt写成“商品图片”“小米手机”等泛化描述,未体现具体型号、颜色、配置。
排查步骤:
- 查看页面源码,筛选所有
<img>标签,检查alt属性; - 对照Google官方指南,确认
alt是否描述“图片本身”,而非“页面主题”; - 使用[axe DevTools](https://www.de