深入 Webpack5 等构建工具系列二(7) - 浏览器兼容性和 browserslist

2,032 阅读12分钟

这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战

为了引出一个叫 postcss-loaderloader,下面我们先来讲一些必要的前端知识。

1. 浏览器的兼容性

  • 我们来思考一个问题:开发中,浏览器的兼容问题,我们应该如何去解决和处理?

    • 当然这个问题很笼统,这里我说的兼容性问题不是指屏幕大小的变化适配
    • 这里的兼容性指的是针对不同的浏览器(不同的版本)支持的特性:比如 css 特性、js 语法之间的兼容性,就是有的浏览器支持而有的浏览器不支持的问题;
  • 我们知道市面上有大量的浏览器:

    • ChormeSafariIEEdgeChrome for AndroidUC BrowserQQ Browser 等等;
    • 它们的市场占有率是多少?我们要不要兼容它们呢?
  • 其实在很多的脚手架配置中,都能看到类似于这样的配置信息:

> 1%
last 2 versions
not dead

如何解决这种兼容性问题呢?

事实上,受益于前端工程化的发展,现在处理这个问题已经很简单了,因为现在已经有各种工具帮助我们处理各种特性了。举个例子,css 里面有一个属性叫做 user-select,这个属性不一定所有浏览器都支持,那么为了让所有浏览器都支持,我们可能会给它加上前缀(甚至使用 csspolyfill 来做特殊处理),那前缀需要手动加吗?当然不需要,因为已经有一些工具能帮助我们自动添加浏览器前缀了,比如 autoprefixer。但是,是否有必要添加浏览器前缀也要看具体情况,比如某个特性如果只需要适配部分浏览器,而这些浏览器都是支持这个特性的,那就没必要添加前缀,不然还让代码变多了,用户下载到的包也会变大。因此,在做适配的时候到底需不需要使用 autoprefixer 这个工具来添加前缀其实是不一定的。类似的,一些 js 特性要不要转换,要不要使用 babel 来做转化也是不一定的,它取决于我们到底要适配哪些浏览器。如果要适配的浏览器需要加前缀那就加,不需要加前缀那就不加。

所以呢,在决定要不要对目标浏览器进行适配之前,我们得先确定个东西,即项目到底支持哪些浏览器。你可能会想,把要支持的浏览器一一列举出来。但这样列举肯定不太好,因为浏览器有很多,各个浏览器的版本也有很多。那我们该怎么做呢?怎么样告诉它当前项目支持哪些浏览器呢?

或许你之前在很多脚手架中(比如 vue3.browserslistrc 文件、reactpackage.json 文件中的 "browserslist" 一项)见过类似这样的配置:

> 1%
last 2 versions
not dead

这些配置其实就是一个个的条件,用来提供给工具(autoprefixerbabel 等)使用,告诉这些工具我现在到底要适配哪些浏览器。

比如上面配置中的 > 1% 就表示适配市场占有率大于 1% 的浏览器,对于 autoprefixer 来说,在这些浏览器中,如果有需要加浏览器前缀的那就加上,如果都不需要加前缀那就都不加;对于 babel 来说,这这些浏览器中,如果支持某个特性那就不需要做转化,如果不支持某个特性那就需要做转化(或者通过 polyfill 的方式增加上一些特性)。

那么问题来了,浏览器的市场占有率等数据从哪里获取呢?那肯定要有一个比较权威、比较专业的地方来对浏览器的信息做统计呀。

2. 浏览器的市场占有率

  • 在哪里可以查询到浏览器的市场占有率呢?

image-20210128212110857.png

3. 认识 browserslist 工具

要知道,从上面这个网站上查询到的浏览器的市场占有率等浏览器信息最后会提供给项目中的很多工具(比如 autoprefixerbabelpostcss-preset-env 等等)进行使用。因此,我们最好有另外一个工具专门用来共享这些浏览器数据,这个工具就是 browserslist 哈。browserslist 工具负责帮助我们查询当前条件匹配到的浏览器的信息,并把查询到的结果共享给所有的工具。

  • 我们如何可以在 css 兼容性和 js 兼容性下共享我们配置的兼容性条件呢?
    • 就是当我们设置了一个条件:> 1%
    • 我们表达的意思是 css 要兼容市场占有率大于 1% 的浏览器,js 也要兼容市场占有率大于 1% 的浏览器
    • 如果我们是通过工具来达到这种兼容性的,比如后面我们会讲到的 postcss-preset-envbabelautoprefixer
  • 如何可以让它们共享我们的配置呢?
    • 答案就是 Browserslist
  • Browserslist 是什么?Browserslist 是一个在不同的前端工具(举例如下)之间,共享目标浏览器和 Node.js 版本的配置
    • Autoprefixer
    • Babel
    • postcss-preset-env
    • eslint-plugin-compat
    • stylelint-no-unsupported-browser-features
    • postcss-normalize
    • obsolete-webpack-plugin

上面的 7 个工具都需要通过 Browserslist 查询到的目标浏览器决定最后到底要转换成什么样的结果。

因此,Browserslist 这个工具对于我们工程化的项目非常重要,这也是为什么 vuereact 项目中都有这个东西的原因。而现在我们讲 webpack,也必须讲这个东西。

4. 浏览器查询过程

  • 我们可以编写类似于这样的配置:
> 1%
last 2 versions
not dead
  • 那么之后,这些工具会根据我们的配置来获取相关的浏览器信息,以方便决定是否需要进行兼容性的支持:

    • 条件查询使用的是 caniuse-lite 工具,这个工具的数据来自于 caniuse 的网站上;

编写完规则后,browserslist 这个工具是从哪里帮助我们进行查询呢?答案是 caniuse 网站,但是在 caniuse 网站上查询时,并不是说 browserslist 这个工具里面有发送网络请求到 caniuse 网站进行查询。事实上,caniuse 里面提供了一个叫做 caniuse-lite 的工具,browserslist 是通过这个工具来进行查询的。比如在 vue3 脚手架中的 node_modules/browserslist/index.js 文件的开头部分你就能看到它 requirecaniuse-lite,最终就是通过 caniuse-lite 这个小工具使用我们传入的条件帮助我们查询匹配的浏览器的。

下面,我们就来演示一下 browserslist 这个工具的使用。那要想使用 browserslist,首先得安装它的包呀,不过我们可以先去看下在安装 webpack 的一些其它东西时有没有安装了 browserslist,我们可以发现已经安装过了。所以我们这里就不需要重新安装了。另外,在 node_modules/.bin 里面也应该是有 browserslist 的可执行文件的。所以,下面我们可以这样来做:在终端中运行如下命令:

npx browserslist "> 1%, last 2 versions, not dead"

如果是在 MacOS 上运行上述命令,应该能直接在终端中看到一个浏览器信息列表,这些浏览器就是和命令中输入的条件匹配的所有浏览器。那意味着之后我们再做诸如 css 特性、js 特性的适配时,针对的就是这些浏览器。但在 Windows 系统上运行上述命令目前不会在终端输出任何信息(之前是可以的),而是会在当前目录下生成一个名为 1% 的文件,里面的内容也和 MacOS 上显示的不一样,可能是 browserslist 升级之后的问题,但没有关系,对此我们不用纠结(因为我们运行上述命令的目的只是为了演示一下这个 browserslist 工具确实能帮助我们查询到浏览器),我们也可以在项目目录下新建 .browserslistrc 文件,在里面把查询条件写好,然后直接在项目目录下执行 npx browserslist,就会自动去读取配置文件(.browserslistrcpackage.json)中的查询条件,并输出结果了,修改 .browserslistrc 文件中的查询条件,再次执行 npx browserslist,会看到不同的结果。

但真实开发时,我们不会在命令行中使用 browserslist,而是会对 browserslist 做一个配置,配置完后用来做共享(项目中某个地方要用到时,就自动去读这个配置文件,然后根据配置去查询出对应的浏览器)。那这个配置文件如何编写呢?我们后面再讲。这里再讲一个东西,即关于上述命令中各个条件之间的关系(交集、并集、非)。

  • 如果没有配置,那么也会有一个默认配置:

image-20210131112934650.png

  • 我们编写了多个条件之后,多个条件之间是什么关系呢?

image-20210131113114467.png

好,下面我们就来讲如果想要在多个工具里面共享 browserslist 配置中的条件的话,这些条件应该如何编写。一共有两种编写方式(选择其中一个即可,建议不要同时使用,否则可能会报错(browserslist: xxx contains both .browserslistrc and package.json with browsers)):

  1. package.json 文件中添加 "browserslist" 字段,对应一个数组,数组中编写一个个条件;
  2. 新建 .browserslistrc 文件,在该文件中编写条件;

注:.browserslistrc 中的 rc 应该是指 runtime compiler,表示其它工具在运行时编译的时候,会读取这个文件。

4.1 browserslist 配置编写方式 1 示例:

image-20210131134100922.png

编写完上图所示的配置之后,再在项目中使用诸如 babelautoprefixer 等工具时,就会自动来到这个 browserslist 对应的数组中查询这些条件,然后从 caniuse-litecaniuse-db 库的精简版本)这个库中查询出来到底要适配哪些浏览器。

上图中的条件之间是并集的关系,如果要取交集,可以这样编写:

image-20210131142649089.png

在两种编写格式下分别运行 npx browserslist 命令,可以看到不同的输出结果:

取并集的写法的输出结果: image-20210131142926946.png

取交集的写法的输出结果: image-20210131143028305.png

4.2 browserslist 配置编写方式 2 示例:

image-20210131144210400.png

在项目目录下新建 .browserslistrc 文件,在其中直接输入条件,换行就表示取并集。如果要取交集,可以这样编写:

image-20210131144635135.png

在两种编写格式下分别运行 npx browserslist 命令,可以看到不同的输出结果:

取并集的写法的输出结果: image-20210131144500978.png

取交集的写法的输出结果:

image-20210131144742003.png

当然,如果两种方式都没有使用,则会采用默认配置 > 0.5%, last 2 versions, Firefox ESR, not dead,前面已经说过。

以上,就是关于 browserslist 编写方式的简述。到此为止,相信大家对浏览器适配在工程化中到底怎么做、 .browserslistrc 到底什么样的作用以及 caniuse 网站在整个环节中起到什么样的作用应该有了比较清楚的认识。

这里再提一个东西,可能你以前在创建 vue 项目的时候(比如 vue create),在其中一个步骤中会遇到一个问题,它会问你想要把配置信息写到 package.json 文件里面还是写到单独的一个配置文件里面,其实这就跟这里的 browserslist 的两种配置方式类似了,即一种方式是写到 package.json 文件中,一种方式是写到一个单独的文件中。

5. Browserslist 的编写规则

那么在开发中,我们可以编写的条件都有哪些呢?(加粗部分是最常用的)

  • defaults:Browserslist 的默认浏览器(> 0.5%, last 2 versions, Firefox ESR, not dead
  • > 5%:通过全局使用情况统计信息选择的浏览器版本(也可以使用 >=<<=
    • 5% in US:使用美国使用情况统计信息。它接受两个字母的国家/地区代码;
    • > 5% in alt-AS:使用亚洲地区使用情况统计信息。有关所有区域代码的列表,请参见 caniuse-lite/data/regions
    • > 5% in my stats:使用自定义用法数据;
    • > 5% in browserslist-config-mycompany stats:使用来自 browserslist-config-mycompany/browserslist-stats.json 的自定义用法数据;
    • cover 99.5%:提供覆盖率的最受欢迎的浏览器;
    • cover 99.5% in US:与上述相同,但国家/地区代码由两个字母组成;
    • cover 99.5% in my stats:使用自定义用法数据;
  • dead:24 个月内没有官方支持或更新的浏览器。现在是 IE 10, IE_Mob 11, BlackBerry 10, BlackBerry 7, Samsung 4OperaMobile 12.1
  • last 2 versions:每个浏览器的最后 2 个版本。
    • last 2 Chrome versions:最近 2 个版本的 Chrome 浏览器。
    • last 2 major versionslast 2 iOS major versions:最近 2 个主要版本的所有次要/补丁版本。
  • node 10 and node 10.4:选择最新的 Node.js 10.x.x10.4.x 版本。
    • current node:Browserslist 现在使用的 Node.js 版本。
    • maintained node versions:所有还在被 Node.js 基金会维护的 Node.js 版本。
  • iOS 7:直接使用 iOS 浏览器版本 7。
    • Firefox > 20:Firefox 的版本高于 20 的。也可以使用 >=<<=。它也可以和 Node.js 一起使用。
    • ie 6-8:选择一个版本的包含范围。
    • Firefox ESR:最新的 [Firefox ESR] 版本。
    • PhantomJS 2.1 and PhantomJS 1.9:选择类似于 PhantomJS 运行时的 Safari 版本。
  • extends browserslist-config-mycompany:从 browserslist-config-mycompany npm 包中查询。
  • supports es6-module:支持特定功能的浏览器。这里的 es6-moduleCan I Use 页面 URL 的 feat 参数。所有可用功能的列表可以在 caniuse-lite/data/features 找到。
  • browserslist config:在 Browserslist 配置中定义的浏览器。在差异服务中很有用,可用于修改用户的配置,例如 browserslist config and supports es6-module
  • since 2015last 2 years:自 2015 年以来发布的所有版本(从 2015-03 以及从 2015-03-10 开始)。
  • unreleased versionsunreleased Chrome versions:alpha 和 beta 版本。
  • not ie <= 8:排除先前查询选择的浏览器(这里表示 ie 版本 <= 8 的就不适配了)。

相关链接