news 2026/9/29 15:07:45

HTML——无障碍访问(一)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HTML——无障碍访问(一)

无障碍访问

    • 1、键盘的无障碍访问
      • 1.1、务必了解的tabindex属性
      • 1.2、深入了解快捷键设置属性accesskey
        • 1.2.1、隐藏的元素能否触发accesskey快捷访问
        • 1.2.2、多个元素使用相同的accesskey属性值会怎样
        • 1.2.3、约定俗成的开发策略
    • 2、屏幕阅读无障碍访问
      • 2.1、尽可能使用原生元素和属性
      • 2.2、系统介绍ARIA规范属性
        • 2.2.1、ARIA role属性
        • 2.2.2、ARIA常规属性
        • 2.2.3、ARIA状态属性

之所以要强调HTML的语义化,一方面是部分HTML自带交互行为,有助于降低Web开发成本;另一方面就是它无障碍访问的能力。

无障碍访问包括很多方面,如颜色的对比度、按钮的大小,以及辅助设备的访问与识别,如鼠标和键盘的访问能力、屏幕阅读器或翻译工具的访问能力、虚拟键盘使用的能力等。这些都需要你对HTML有所了解才能做得出色。

由于本书是介绍HTML的,因此仅会介绍与之相关的无障碍访问,包括物理键盘、屏幕阅读器及移动设备虚拟键盘。

1、键盘的无障碍访问

对于Web网页而言,键盘的访问能力是最基本的,因为不仅视力有障碍的人群需要网页有键盘访问能力,即使是正常用户,也需要网页中的控件元素可以通过键盘访问,如按回车键提交表单、下拉列表的上下键选取等。随着智能电视的普及,在电视机上也能访问网页,此时遥控器就扮演了键盘的角色,如果网页内容无法被键盘访问,那么该网页在电视机上就不可用。

其实,做好网页的键盘无障碍访问特性很简单,遵循以下原则即可:所有具有点击行为的控件元素一定要使用<a>元素或者<button>元素,如果能做到这一点,那么即使没有鼠标,网页也是基本可用的,因为这两个元素天然可以被Tab键索引,天然支持按回车键触发点击行为。

如果万不得已使用<div>元素或<span>元素模拟按钮,那么一定要设置tabindex=“0”,值必须是0,至于原因,且听我慢慢道来。

1.1、务必了解的tabindex属性

对于键盘访问,最重要的HTML属性莫过于tabindex属性了,这是一个全局属性,顾名思义,就是Tab+index,也就是可以使用Tab键索引元素。该属性可以让任意的元素变成键盘可访问元素,支持的值包括-1、0,以及1~32767的整数,如果属性值超过32767,则会按照-1渲染。

不同的tabindex属性值有着不同的含义。

  • 当tabindex属性值为-1时,元素可以获得焦点(点击或执行JavaScript的focus()方法)​,但是无法被Tab键索引。该值常用来转移焦点,例如可以将弹窗上的元素设置为tabindex=“-1”,这样当弹窗出现时,焦点就会自动转移到弹窗上,而不是弹窗后的页面上。

    不过原生的<dialog>元素并不需要这么处理,因为它本身就支持焦点转移,并且对于<dialog>元素而言,tabindex属性是无效的。

    tabindex="-1"的另外一个经典应用就是iOS Safari浏览器的无法失焦问题,例如如下代码所示的<a>元素在点击之后会被聚焦(focus),但是此时你再点击页面空白区域,该元素是不会失焦的。

    解决方法就是找到层级足够高的祖先元素(不能是<body>元素,可以是<body>子元素)​,然后设置tabindex=-1,任意的非控件HTML元素在设置了tabindex=-1属性后,点击的时候都是会有聚焦状态的。

    也就是点击空白区域,会把原本附着在链接上的焦点转移到祖先元素上,于是blur失焦就正常触发了。

    不过,根据测试这个持续了近10年的体验问题在iOS 14之后被修复了。

  • 当tabindex属性值为0时,元素可以被Tab键索引,且索引顺序和<input>、<button>这类控件元素一致。

  • 当tabindex属性值是自然数的时候,则

    • 对于IE、Edge及N年前的Chrome等浏览器,tabindex是自然数的元素的索引顺序级别最高,然后才是tabindex=0的元素。
    • 在现在主流浏览器下,tabindex=0的元素的索引顺序级别最高,tabindex是自然数的元素的索引顺序靠后,甚至比浏览器窗体自身的控件元素的聚焦顺序还要靠后。表现为
      • tabindex="0"的元素最先被聚焦,随后浏览器自身各个按钮和地址栏被聚焦,最后才是tabindex="1"的元素被聚集。
      • tabindex="1"的元素在通过Tab键索引后聚焦的是tabindex="0"的元素,tabindex="0"的元素在按下Shift+Tab键后聚焦的是tabindex="1"的元素。
      • tabindex=2的元素一定比tabindex=1的元素的索引值靠后。

基本上,tabindex属性要么被设置为-1,要么被设置为0,设置为自然数的场景现在已经很少见了。

1.2、深入了解快捷键设置属性accesskey

可能很多读者都不知道,只要在HTML代码上设置一个属性,就可以让浏览器支持我们自定义的快捷访问方式,而这个神奇的属性就是本节要介绍的accesskey属性。

在HTML 4.0.1中,accesskey属性可以作用在以下元素上:<a>、<area>、<button>、<input>、<label>、<legend>及<textarea>。在HTML 5中,accesskey属性可以作用在任意元素上,变成了HTML全局属性。

例如,作用在<div>元素上:

这里的属性值“1”对应的就是键盘上的数字1,如果是accesskey=“a”,则“a”对应的就是键盘上的字母a。

需要注意的是,虽然accesskey属性值和键盘相对应,但并不是说直接按下这个键就能快捷访问。浏览器是通过快捷键组合访问的方式进行页面元素的快速访问的,具体的组合方式参见下表(注意,随着浏览器的迭代,快捷键的组合方式可能会有调整)​。

IE浏览器和Chrome浏览器的组合键是一样的,但是其交互行为却和其他浏览器都不一样,例如:

在Windows操作系统下,按下Alt+1组合键,IE浏览器只是让<a>元素获得焦点,但是其他所有浏览器都会直接触发click行为。在我看来,IE浏览器的这种行为是不友好的,原因在于,类似<a>、<button>这样的元素还可以在获得焦点后按下回车键触发click行为,但是<div>这类元素是无法通过键盘触发点击行为的,这就使<div>等元素设置的accesskey属性没有任何价值。相比之下,Chrome等浏览器就没有这个问题,如以下HTML代码:

此时,我在Windows操作系统下,按下Alt+3组合键,结果“测试”二字变成了红色,如图所示。

这说明直接触发了绑定的click点击事件。

1.2.1、隐藏的元素能否触发accesskey快捷访问

对于一个元素,如果CSS display属性的计算值是none,则无法通过Tab键进行索引聚焦。那么,设置的accesskey快捷访问是否可以访问呢?

根据我的测试,Chrome浏览器和Firefox浏览器是可以的,元素即使隐藏,只要设置了accesskey快捷访问,按下对应组合键的时候就能触发click行为。例如:


在Windows操作系统下,按下Alt+1组合键,页面会直接刷新。

1.2.2、多个元素使用相同的accesskey属性值会怎样

例如,有如下所示的HTML代码:

在Chrome浏览器下,第一行<div>元素的accesskey属性设置会被忽略,第二行的文字变成了红色,说明accesskey属性被重复设置的表现以后面的元素为准。

而在Firefox浏览器下则两败俱伤,两行<div>元素进行快捷键匹配的时候都不能触发点击事件,变成了单纯的focus索引,出现了聚焦虚框,如图所示,行为表现和IE浏览器一致。

从上面的测试结果可以看出,设置多个元素的accesskey属性值相同是一个比较严重的问题,应当极力避免。

1.2.3、约定俗成的开发策略

accesskey属性的默认访问策略并不实用,一是用户并不知道开发者在页面中藏了哪些快捷访问按键,二是组合快捷键的学习成本实在太高,因此,经过多年的发展,业界已经有了一些约定俗成的开发策略。

  • 其一,舍弃组合快捷键,使用JavaScript进行一些自定义,让用户通过直接按下accesskey属性对应的值就可以访问对应的链接,或者激活对应的元素。
  • 其二,对某些操作的快捷键进行约定俗成的定义,其中最著名、影响最广的就是所有的搜索输入框都使用斜杠字符“/”作为访问快捷键,下图所示的就是脸书网站的accesskey属性设置。

    不仅是脸书,像推特、MDN文档都使用“/”作为访问快捷键,也就是你只要按下“/”键,搜索框就会自动聚焦。

我认为国内的网站也都可以使用此策略,其他的快捷键先不要管,就把搜索匹配用上,只要大家都这么使用,就会形成约定俗成的规范,对于用户而言,其实是一件好事。

2、屏幕阅读无障碍访问

在美国,如果网页无法让残疾人正常访问,企业可能会被要求重新设计网站,不仅涉及金钱损失,还要支付律师费。国内前几年也出台了老龄化适配政策,随后也出现了专门支持无障碍访问的公司,可见对无障碍访问的学习还是很有必要的,不求精通,至少要掌握一些基础知识。

2.1、尽可能使用原生元素和属性

目前,国内的Web产品对无障碍访问的支持比较糟糕,以至于,你只要在网页开发的时候,尽可能使用语义精准的HTML元素,所实现的产品的无障碍访问能力已经超过绝大多数公司的产品。

下面介绍关于Web无障碍访问的一些常识性的知识,因为很多人在这方面容易犯错。

  • 有些按钮没有文字,仅有一个小图标,很多人图省事就使用一个<span>元素,这是糟糕的做法。外面一定要嵌套<button>按钮,如果是链接,则可以使用<a>元素。

  • 桌面端网页有很多鼠标悬停显示下拉列表的效果,这些效果一定要同时设置点击切换显隐,否则触摸屏、屏幕阅读器或者键盘访问就会有障碍。

  • 弹出层或者弹出框一定要有关闭按钮,按钮最好位于右上角,如果这和视觉设计稿不符,可以设置透明度为0,这对视力有障碍的用户会非常友好。

  • 所有的控件元素在聚焦的时候一定要有样式变化,建议使用outline添加轮廓,下面这样的CSS设置务必避免:

  • 图形化的内容一定要设置文字内容,尤其是图标内容。以VoiceOver为例(iOS、MAC OS X操作系统内置屏幕阅读器)​,下面的HTML代码是指不会有任何内容被朗读的(只有按钮元素的title属性值会被朗读)​:

    但如果里面有文字,那么视力有障碍的用户就能识别,例如:

  • 只要不是无关紧要的装饰性图片,一定要使用<img>元素,且一定设置alt描述值。如果图片有角标等信息,则可以一并写在alt属性中,比如图所示的角标效果,就可以使用如下所示的HTML代码:

  • 不要单独使用SVG图像,因为哪怕设置<title>元素,也是不会被读取的,例如:

    其中的“返回”二字并不会被读取,而是会朗读宽泛的“图像”​。不过如果外面有链接元素,则在VoiceOver下就会朗读“返回,链接”​,因为此时SVG从图像性质变成了链接内容。


    或者使用下一节介绍的ARIA规范属性aria-label:

    此时,辅助设备就会朗读“图像 返回”​。

    大家也可以开启VoiceOver(亦叫“旁白”​)试试,想象自己是一个视力有障碍的用户,测试一下自己目前所开发的Web页面能不能正常体验,这是提升自己无障碍访问开发能力最快速的方法。

2.2、系统介绍ARIA规范属性

除原生的HTML元素和属性外,我们还可以用ARIA规范定义的HTML属性增强Web产品的无障碍访问能力。从功能上区分,这些属性可以分为三类:ARIA role属性、ARIA常规属性和ARIA状态属性,下面逐一进行介绍。

2.2.1、ARIA role属性

ARIA role属性可以让没有语义的HTML元素变成有语义的元素,例如:

此时,屏幕阅读器就会朗读“按钮 点击”​,而不仅仅是“点击”​,提升了无障碍访问能力。常用的role属性值包括下面这些。

  • role=“alert”
    表示警告,例如Ajax数据请求失败在页面顶部或中间位置出现的提示信息:
  • role=“alertdialog”
    表示弹出对话框,和role="alert"的区别在于,alertdialog会有“关闭”或“确定”按钮,例如:

    视觉表现见下图。
  • role=“application”
    表示应用,例如自定义的时间选择器。
  • role=“banner”
    表示网站的头部,代替<header>元素的角色,一个页面只能有一个,例如:
  • role=“button”
    表示按钮,很好理解,例如:

    此role属性值多和tabindex="0"一起使用,保证键盘的无障碍访问。
  • 网格与表格
    与网格和表格相关的role属性值较多,因此将它们放在一起介绍,详见下表。

    下面这段代码示意了以上部分role属性值的使用:

  • role=“checkbox”
    表示复选框,例如:

    视觉表现如图所示。
  • role=“combobox”
    表示下拉列表组合框,例如:


    视觉表现如图所示。
  • role=“command”
    表示指令,纯操作提示,没有任何输入行为,例如富文本编辑器中的Ctrl+Z组合键为撤销、Ctrl+B组合键为加粗、Ctrl+C组合键为复制等。
  • role=“complementary”
    表示补充内容,类似于<aside>元素,例如:
  • role=“contentinfo”
    表示网站的页脚,类似于<footer>元素,例如:
  • role=“definition”
    表示定义,类似于<dfn>元素,例如:
  • role=“dialog”
    表示对话框,类似于元素,多用在自定义对话框组件上。
  • role=“document”
    表示复杂小部件或应用程序中的可聚焦内容,多为描述信息,辅助设备可上下切换阅读。例如:
  • 文档结构角色
    有不少role属性值可用来表述文档结构信息,各个属性值、描述及对应的语义化元素如表所示。
  • role=“feed”
    表示订阅源列表的容器,订阅作品需要支持通过上下键进行定位浏览,例如:

  • role=“form”
    表示表单,类似于<form>元素。
  • role=“generic”
    表示通用的元素,也就是没有语义的元素,类似于<div>、<span>元素。
  • role=“group”
    表示分组,例如:
  • role=“input”
    表示允许输入的小部件。
  • role=“landmark”
    表示标识,例如在摘要中扮演导航的角色,以方便辅助设备快速导航和查找内容。
  • role=“link”
    表示链接,例如:
  • role=“log”
    表示日志记录,例如:
  • role=“main”
    表示主要内容,类似于<main>元素。
  • role=“marquee”
    表示滚动内容,例如直播时出现的滚动信息列表:
  • role=“math”
    表示数学公式,例如:

    此值多用在MathML语言无法使用的场景下。
  • menu系列
    菜单栏、菜单、菜单子项均有对应的role属性值,详见下表。

    代码表示使用示意:

    视觉表现如图所示。
  • role=“navigation”
    表示导航,类似于<nav>元素。
  • role=“none”
    role="presentation"的另外一种写法。
  • role=“note”
    表示注释,类似于<blockquote>元素。
  • role=“option”
    表示listbox的可选项,例如:

    如果给内容设置role=“option”,则辅助设备会将里面的内容作为一个整体进行一次性阅读。
  • role=“presentation”
    用在让原来有语义的元素变得没有语义的场景下,例如:

    等同于:
  • role=“progressbar”
    表示进度条,例如:

    视觉表现如图所示。
  • role="radio"和role=“radiogroup”
    表示单选按钮和单选按钮组,例如:

    视觉表现如图所示。
  • role=“range”
    表示范围,想要自定义类似<meter>、<progress>及<input/range>元素的时候可以考虑此值。
  • role=“region”
    表示区域,类似于<section>元素。
  • role=“scrollbar”
    表示可触摸滚动条,例如<input/range>元素的滚动条。
  • role=“search”
    表示搜索,类似于<search>元素。例如:

    建议使用:
  • role=“searchbox”
    表示搜索输入框,类似于<input epub:type=“search”>元素。例如:
  • role=“select”
    表示选择,类似于<select>元素。
  • role=“separator”
    表示分隔符,类似于<hr>元素。
  • role=“slider”
    表示滑块,类似于<input epub:type=“range”>元素。
  • role=“spinbutton”
    表示可上下微调的范围选择元素,<inputepub:type=“number”>元素就符合这个特质,例如鼠标经过数值输入框的时候,右侧会显现上下数值调整按钮,如图所示。
  • role=“structure”
    表示结构化的文档元素。
  • role=“suggestion”
    表示建议内容,推荐使用<ins>元素和<del>元素。
  • role=“switch”
    表示开关,例如:

    视觉表现如图所示。
  • tab选项卡系列
    选项卡、选项卡列表、选项卡面板均有对应的role属性值,详见下表。

    例如:

    视觉表现如图所示。
  • role=“textbox”
    表示文本输入框,类似于<input epub:type=“text”>元素,如果同时设置了aria-multiline=“true”,则等同于<textarea>元素。
  • role=“timer”
    表示计时器,例如:
  • role=“toolbar”
    表示工具栏,用在一系列操作按钮的容器元素上。
  • role=“tooltip”
    表示悬浮提示元素。
  • role=“widget”
    表示小部件。
  • role=“window”
    表示窗口。
2.2.2、ARIA常规属性

ARIA常规属性有很多,这里只介绍一些常用的。

  • aria-activedescendant
    表示当前活动的子元素,值为后代元素的id值,例如:

    在此HTML示例中,aria-activedescendant属性定义了当工具栏获取焦点时,工具栏的第一个控件(拥有id=“button1”)是能获取焦点的子控件。
  • aria-atomic
    表示区域内容是否完整播报,值可以为true和false。当其值为true时,表示辅助设备需要把整个区域内容都通报给使用者;当其值为false时,则表示只需要通报修改的部分。例如:

    这里的aria-atomic属性值为true则表示当时间改变的时候,这里的年月日要完整播报,不要只改了月份就只报月份的内容。
  • aria-autocomplete
    表示用户文本框的自动提示是否提供。可选值有inline、list、both和none。

需要注意的是,如果aria-autocomplete="list/inline/both"被设置在支持autocomplete的元素上,则autocomplete属性值需要被设置为“on”​;如果是aria-autocomplete=“none”,则需要被设置为“off”​。

  • aria-busy
    表示当前区域的忙碌状态,默认为false,表示清除busy状态;可选为true,表示该区域正在加载;或为error,表示该区域验证无效。

  • aria-controls
    表示当前区域控制的元素,值为目标元素的id值,例如:

  • aria-current
    表示当前元素的状态,可选值有page、step、location、date、time、true和false。

  • aria-description
    表示当前区域的描述信息,值为描述内容,例如:

    该属性类似于aria-label,不过aria-label描述的是简单的信息,aria-description描述的是长句子。

  • aria-describedby
    表示当前区域的描述信息,值为描述信息元素的id值,例如:

    如果值指定了不止一个id,那么所有元素会合并在一起共同创建一条单独的描述。

  • aria-details
    表示当前区域更加详细的描述信息。aria-describedby描述的是纯文本,而aria-details描述的信息可以有图片,甚至包括交互内容。

  • aria-flowto
    表示当前区域的下一个区域,值为目标元素的id值。

  • aria-haspopup
    表示当前区域是否有弹出框,支持多种属性值,具体如下。

    • false:没有弹出框。
    • true:有弹出框,语义同menu属性值。
    • menu:有弹出菜单。
    • listbox:有弹出列表。
    • tree:有弹出树形结构。
    • grid:有弹出表格。
    • dialog:有弹出对话框。

    此属性比较常用,因为在目前的Web开发中,自定义弹出框、弹出层组件还是非常常见的。

  • aria-keyshortcuts
    表示当前区域的快捷键,值为快捷键内容,例如:

  • aria-label
    表示交互元素的描述信息,值为描述内容,例如:

  • aria-labelledby
    表示交互元素的描述信息,值为描述信息元素的id值,例如:

  • aria-level
    表示当前区域的等级或层级,值为数字。aria-level常用在role="heading"元素上,表示对应的标题的级别,例如:

    语义上等同于元素<h3>。

  • aria-live
    表示当前区域的内容是否需要实时更新,值为off(默认值)​、polite和assertive。

    当值为polite时,表示在内容更新时,辅助设备会在用户操作完成后播报;当值为assertive时,表示在内容更新时,辅助设备会立即播报。

  • aria-modal
    表示当前区域是否是模态对话框(有半透明黑色背景)​,值为true或false。

  • aria-multiline
    表示当前区域是否支持多行文本,值为true或false。

  • aria-multiselectable
    表示当前区域是否支持多选,值为true或false。

  • aria-orientation
    表示当前区域的方向,值为horizontal或vertical。

  • aria-owns
    表示当前区域拥有的元素,值为目标元素的id值。

  • aria-placeholder
    表示当前区域的占位符,值为占位符内容。

  • aria-posinset
    表示当前区域在父元素中的位置,值为数字。

  • aria-relevant
    表示当前区域的内容更新方式,值为additions、removals、text、all(等同于additions removals text)和additions text(默认值)​。
    其中各个值的含义如下。

    • additions:表示添加内容时播报。
    • removals:表示删除内容时播报。
    • text:表示内容改变时播报。
    • all:表示添加、删除、改变内容时都播报。
  • aria-roledescription
    表示定义当前元素角色信息的描述,值为角色描述内容。例如:

    此时,屏幕阅读器会阅读“季度报告 幻灯片”​,而不是“季度报告 文章”​。

  • aria-setsize
    表示当前区域的子元素个数,值为数字。例如:

  • aria-sort
    表示当前区域的排序方式,值为ascending、descending、none(默认值)和other。

  • aria-valuemax、aria-valuemin和aria-valuenow
    表示当前区域的最大值、最小值和当前值,值为数字。对应元素输入框元素的max、min和value属性,使用示意:

  • aria-valuetext
    aria-valuetext属性的作用是设置比aria-valuenow属性更为友好的文本朗读内容。例如在自定义下拉列表中,aria-valuenow往往是选项的id,但是朗读的内容应该是对应的文本描述,此时就可以使用aria-valuetext。

2.2.3、ARIA状态属性

ARIA状态属性也有很多,其数组值都是true或false,常用的状态属性参见下表。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 15:07:27

ComfyUI本地部署MiniMax H3图生视频:从8GB显存到全系显卡加速实践

这次我们来看一个小白向的 ComfyUI 本地视频生成流程&#xff1a;用海螺 MiniMax H3 跑图生视频。很多朋友关心 8GB 显存到底能不能玩、10 系到 50 系显卡各自怎么加速、工作流怎么加载、模型怎么放、批量任务和 API 怎么接。这篇文章统一梳理一遍。 MiniMax H3 是视频生成模型…

作者头像 李华
网站建设 2026/9/29 15:05:33

国庆出行蓝牙耳机怎么选?蓝牙5.4、ANC降噪与单次续航的技术选型分析

国庆出行选蓝牙耳机&#xff0c;优先级排序为——单次续航 > ANC/ENC降噪类型 > 佩戴重量 > 蓝牙版本 > 音质。 充电仓的“综合续航”在旅途中意义有限&#xff0c;耳机本体的单次续航才是决定能否从出发地撑到目的地的关键指标。以下是针对不同预算和使用场景的5款…

作者头像 李华
网站建设 2026/9/29 15:04:27

元宝 LeetCode 129. 求根节点到叶节点数字之和 Rust实现

这是 LeetCode 129 题 “求根节点到叶节点数字之和” 的 Rust 实现。 题目描述 给你一个二叉树的根节点 “root”&#xff0c;树中每个节点都存放有一个 “0” 到 “9” 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff08;例如路径 “1 -> 2 -> 3” 表示…

作者头像 李华
网站建设 2026/9/29 15:04:21

搜推一体架构设计:搜索与推荐怎么融合?四层架构与四阶段落地

摘要&#xff1a;搜推一体架构的关键不是合并两个系统&#xff0c;而是把数据层、召回层共用到底座上&#xff0c;只在排序与策略层分场景分离&#xff1b;落地顺序为统一事件流 → 共用向量召回 → 分场景排序 → 策略联动。 搜索与推荐各建一套索引、各记一份日志&#xff0c…

作者头像 李华
网站建设 2026/9/29 15:03:57

前端异常监控体系搭建:捕获、格式化到上报的完整方案

前两天线上出了个问题&#xff1a;用户点某个按钮页面直接白屏&#xff0c;群里反馈了好几条消息&#xff0c;我在本地试了半天也没复现。最后查日志才发现&#xff0c;错误在 low-end 机型上偶发&#xff0c;而代码里唯一留下的线索就是一行 console.log(error) 。问题是&am…

作者头像 李华