Polyfill方式解决前端兼容性问题:core-js包结构与各种配置策略

作者:漂流瓶jz日期:2026/1/20

简介

在之前我介绍过Babel:解锁Babel核心功能:从转义语法到插件开发,Babel是一个使用AST转义JavaScript语法,提高代码在浏览器兼容性的工具。但有些ECMAScript并不是新的语法,而是一些新对象,新方法等等,这些并不能使用AST抽象语法树来转义。因此Babel利用core-js实现这些代码的兼容性。

core-js是一个知名的前端工具库,里面包含了ECMAScript标准中提供的新对象/新方法等,而且是使用旧版本支持的语法来实现这些新的API。这样即使浏览器没有实现标准中的新API,也能通过注入core-js代码来提供对应的功能。

像这种通过注入代码实现浏览器没有提供的API特性,叫做Polyfill。这个单词的本意是填充材料,在JavaScript领域中,这些注入的代码就类似“填充材料”一样,帮助我们提高代码的兼容性。另外core-js还提供了一些还在提议中的API的实现。

core-js使用方式

使用前后对比

要想看到core-js使用前后的效果对比,首先需要确定某个特性和对应的执行环境,在这个环境中对应的特性不存在。我本地是Node.js v18.19.1版本,这个版本并没有实现Promise.try这个方法,因此我们就用这个方法进行实验。首先是没有引入core-js的场景:

1Promise.try(() => {
2  console.log('jzplp!')
3})
4
5/* 执行结果
6Promise.try(() => {
7           ^
8TypeError: Promise.try is not a function
9*/
10

可以看到没有引入core-js,直接使用Promise.try时,会因为没有该方法而报错。然后再试试引入core-js的效果:

1require('core-js')
2Promise.try(() => {
3  console.log('jzplp!')
4})
5
6/* 输出结果
7jzplp!
8*/
9

可以看到引入core-js后,原本不存在的API被填充了,我们的代码可以正常执行并拿到结果了。这就是core-js提高兼容性的效果。

单个API引入

core-js不仅可以直接引入全部语法,还可以仅引入单个API,比如某个对象或某个方法。首先看下只引入Promise对象:

1// require('core-js/full') 等于 require('core-js')
2require('core-js/full/promise')
3Promise.try(() => {
4  console.log('jzplp!')
5})
6
7/* 输出结果
8jzplp!
9*/
10

然后再看下直接引入对象中的某个方法:

1require('core-js/full/promise/try')
2Promise.try(() => {
3  console.log('jzplp!')
4})
5
6/* 输出结果
7jzplp!
8*/
9

不注入全局对象

前面展示的场景,core-js都是将API直接注入到全局,这样使用这些API就如环境本身支持一样,基本感受不到区别。但如果我们不希望直接注入到全局时,core-js也提供了使用方式:

1const promise = require('core-js-pure/full/promise');
2promise.try(() => {
3  console.log('jzplp!')
4})
5Promise.try(() => {
6  console.log('jzplp!2')
7})
8/* 输出结果
9jzplp!
10Promise.try(() => {
11           ^
12TypeError: Promise.try is not a function
13*/
14

可以看到,使用core-js-pure这个包之后,可以直接导出我们希望要的API,而不直接注入到全局。此时直接使用全局对象方法依然报错。而core-js这个包虽然也能导出,但它还是会直接注入全局,我们看下例子:

1const promise = require('core-js/full/promise');
2promise.try(() => {
3  console.log('jzplp!')
4})
5Promise.try(() => {
6  console.log('jzplp!2')
7})
8
9/* 输出结果
10jzplp!
11jzplp!2
12*/
13

因此,如果希望仅使用导出对象,还是需要使用core-js-pure这个包。core-js-pure也可以仅导出对象方法:

1const try2 = require("core-js-pure/full/promise/try");
2Promise.try = try2;
3Promise.try(() => {
4  console.log("jzplp!");
5});
6
7/* 输出结果
8jzplp!
9*/
10

因为导出的对象方法不能独立使用,因此在例子中我们还是将其注入到Promise对象后使用。

特性分类引入

core-js中包含非常多API特性的兼容代码,有些是已经稳定的特性,有些是还处在提议阶段的,不稳定的特性。我们直接引入core-js会把这些特性全部引入,但如果不需要那些不稳定特性,core-js也提供了多种引入方式:

  • core-js 引入所有特性,包括早期的提议
  • core-js/full 等于引入core-js
  • core-js/actual 包含稳定的ES和Web标准特性,以及stage3的特性
  • core-js/stable 包含稳定的ES和Web标准特性
  • core-js/es 包含稳定的ES特性

这里我们举两个例子尝试下。首先由于ECMAScript标准一直在更新中,有些特性现在是提议,未来可能就已经被列入正式特性了。因此这里的例子需要明确环境和core-js版本。这里我们使用Node.js v18.19.1和core-js@3.47.0版本,以写这篇文章的时间为准。

首先第一个特性是:数组的lastIndex属性,这是一个stage1阶段的API,这里针对不同的引入方式进行尝试:

1// 不引入core-js尝试
2const arr = ["jz", "plp"];
3console.log(arr.lastIndex);
4/* 输出结果
5undefined
6*/
7
8// 引入core-js/full
9require("core-js/full");
10const arr = ["jz", "plp"];
11console.log(arr.lastIndex);
12/* 输出结果
131
14*/
15
16// 引入core-js/actual
17require("core-js/actual");
18const arr = ["jz", "plp"];
19console.log(arr.lastIndex);
20
21/* 输出结果
22undefined
23*/
24

首先当不引入core-js时,因为不支持这个API,所以输出undefined。core-js/full支持stage1阶段的API,可以正确输出结果。但core-js/actual仅支持stage3阶段的API,因此还是不支持这个API。

然后我们再看下另外一个API,数组的groupBy方法。这是一个stage3阶段的API:

1// 不引入core-js尝试
2const arr = [
3  { group: 1, value: "jz" },
4  { group: 2, value: "jz2" },
5  { group: 1, value: "plp" },
6];
7const arrNew = arr.groupBy(item => item.group);
8console.log(arrNew)
9/* 输出结果
10const arrNew = arr.groupBy(item => item.group);
11                   ^
12TypeError: arr.groupBy is not a function
13*/
14
15// 引入core-js/actual
16require("core-js/actual");
17const arr = [
18  { group: 1, value: "jz" },
19  { group: 2, value: "jz2" },
20  { group: 1, value: "plp" },
21];
22const arrNew = arr.groupBy(item => item.group);
23console.log(arrNew)
24/* 输出结果
25[Object: null prototype] {
26  '1': [ { group: 1, value: 'jz' }, { group: 1, value: 'plp' } ],
27  '2': [ { group: 2, value: 'jz2' } ]
28}
29*/
30
31// 引入core-js/stable
32require("core-js/stable");
33const arr = [
34  { group: 1, value: "jz" },
35  { group: 2, value: "jz2" },
36  { group: 1, value: "plp" },
37];
38const arrNew = arr.groupBy(item => item.group);
39console.log(arrNew)
40/* 输出结果
41const arrNew = arr.groupBy(item => item.group);
42                   ^
43TypeError: arr.groupBy is not a function
44*/
45

可以看到,不引入core-js时不支持,引入了core-js/actual(包含stage3阶段的API)后支持并能输出正确的结果。core-js/stable中不支持又报错了。

core-js源码结构

前面描述了很多core-js的引入方式,这里我们看一下源码结构,看看core-js内部是如何组织的。

core-js源码目录

1core-js
2├─actual
3   ├─array
4     ├─at.js
5     ├─concat.js
6     └─...
7   ├─set
8     └─...
9   └─...
10├─es
11   └─...
12├─features
13   └─...
14├─index.js
15└─...
16

首先列出core-js源码目录的示意图,可以看到core-js内部有很多目录,对应前面的各种引入方式。这里我们列出每个目录的内容:

  • actual 包含稳定的ES和Web标准特性,以及stage3的特性
  • es 包含稳定的ES特性
  • features 没有说明,猜测和full类似
  • full 所以特性包括早期提议
  • internals 包内部使用的逻辑
  • modules 实际特性的代码实现
  • proposals 包含提议的特性
  • stable 包含稳定的ES和Web标准特性
  • stage 按照stage阶段列出提议特性
  • web 包含Web标准特性
  • configurator.js 是否强制引入逻辑,后面会描述
  • index.js 内容为导出full目录,因此导入core-js等于导入core-js/full

层层引用

在目录中actual, es, full, stable, es是我们已经介绍过的。另外还有web目录仅包含web标准的特性,features和full类似(index.js中直接导出full目录)。

proposals目录包含提议的特性,以特性名来命名文件名。而stage目录中包含0.js, 1.js, 2.js等等,是根据stage阶段来整理的,方便整理和引入对应阶段的特性。

这样整理目录虽然清晰,但这些目录中的特性都是重复的,不可能在每个目录中把特性都实现一遍。因此上面这些目录的文件中,存放的都是实现的引用,并不是特性代码实现本身。真正的实现在modules目录中。modules目录中是以特性名作为命名的文件,文件有固定的前缀名:es.表示ES标准;esnext.表示提议中的标准;web.表示web标准。

这里以我们上面提到过的两个特性为例,看看引用路径,首先是Promise.try:

  • 使用者引入 core-js/full/promise/try.js
  • 引入 actual/promise/try.js
  • 引入 actual/promise/try.js
  • 引入 stable/promise/try.js
  • 引入 es/promise/try.js
  • 最终引入 modules/es.promise.try.js

然后是groupBy方法:

  • 使用者引入 core-js/actual/array/group-by.js
  • 最终引入 modules/esnext.array.group-by.js

可以看到,core-js内部的特性是经过层层引入,最终引入具体的实现代码的。

core-js-pure与core-js-bundle

除了core-js之外,core-js-pure与core-js-bundle这两个包也提供了兼容性。core-js-pure内部的目录结构与core-js一致,只不过core-js-pure不将特性注入到全局。core-js-bundle比较特殊,它是将core-js代码经过打包后再提供,它的结构如下:

1core-js-bundle
2├─index.js
3├─minified.js
4├─minified.js.map
5└─...
6

其中index.js是打包过后的特性集合代码,minified.js是经过压缩混淆后的代码。core-js-bundle只能全部引入并注入到全局,不能引入部分目录或者导出某个属性。

打包和浏览器效果

创建Webpack示例

首先创建一个Webapck项目,方便后续打包查看效果。首先执行:

1# 创建项目
2npm init -y
3# 安装webpack依赖
4npm add webpack webpack-cli html-webpack-plugin
5# 安装core-js依赖
6npm add core-js core-js-pure core-js-bundle
7

创建src/index.js,内容如下:

1const arr = ["jz", "plp"];
2console.log(arr.lastIndex);
3

在package.json文件的scripts中增加命令:"build": "webpack"。最后是Webpack配置文件webpack.config.js:

1const path = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports = {
5  mode: 'production', // 生产模式
6  entry: './src/index.js', // 源码入口
7  plugins: [
8    new HtmlWebpackPlugin({ // 生成HTML页面入口
9      title: 'jzplp的core-js实验', // 页面标题
10    }),
11  ],
12  output: {
13    filename: 'main.js', // 生成文件名
14    path: path.resolve(__dirname, 'dist'),  // 生成文件目录
15    clean: true, // 生成前删除dist目录内容
16  },
17};
18

命令行运行npm run build,即可使用Webpack打包。在dist目录中生成了两个文件,一个是main.js,里面是打包后的js代码;index.html可以让我们在浏览器查看效果。由于我们没有引入core-js,浏览器没有预置lastIndex这个提议中的特性,因此输出undefined。

core-js打包

这里引入core-js,然后打包查看效果。首先是全量引入:

1require("core-js");
2const arr = ["jz", "plp"];
3console.log(arr.lastIndex);
4

此时浏览器输出1,表示core-js注入成功,lastIndex特性生效了。但是我们查看main.js,发现居然有267KB。这是因为它把所有特性都引入了。

如果引入require("core-js/full/array"),此时新特性也可以生效。因为只引入了数组相关特性,因此main.js的大小为59.3KB,比全量引入小很多。

如果引入require("core-js/full/array/last-index"),此时新特性也可以生效。因为只引入了这一个特性,因此main.js的大小为12.2KB。

Babel与core-js

从前面打包的例子中可以看到,core-js整个打包进项目中是非常巨大的,可能比你正常项目的大小还要更大。这样明显会造成占用资源更多,页面加载时间变慢等问题。一个解决办法是,只引入我们代码中使用到的特性,以及我们要适配的浏览器版本中不兼容的特性,用不到的特性不打包进代码中。Babel就提供了这样的功能。

创建Babel示例

1# 创建项目
2npm init -y
3# 安装webpack依赖
4npm add @babel/core @babel/cli @babel/preset-env
5# 安装core-js依赖
6npm add core-js core-js-pure core-js-bundle
7

创建src/index.js,内容如下:

1require('core-js');
2const jzplp = 1;
3

在package.json文件的scripts中增加命令:"babel": "babel src --out-dir lib"。最后是Babel配置文件babel.config.json:

1{
2  "presets": [
3    [
4      "@babel/preset-env",
5      {
6        "targets": {
7          "chrome": "100"
8        }
9      }
10    ]
11  ]
12}
13

targets中表示我们需要兼容的浏览器版本。执行npm run babel,生成结果再lib/index.js中,内容如下。可以看到未对core-js做任何处理。

1"use strict";
2
3require('core-js');
4const jzplp = 1;
5

preset-env配置entry

@babel/preset-env是一个Babel预设,可以根据配置为代码增加兼容性处理。前面创建Babel示例时已经增加了这个预设,但是没有增加core-js配置。这里我们加一下:

1{
2  "presets": [
3    [
4      "@babel/preset-env",
5      {
6        "targets": {
7          "chrome": "100"
8        },
9        "useBuiltIns": "entry",
10        "corejs": "3.47.0"
11      }
12    ]
13  ]
14}
15

这里增加了corejs版本和useBuiltIns配置,值为entry。配置这个值,会使得@babel/preset-env根据配置的浏览器版本兼容性,选择引入哪些core-js中的特性。这里再执行命令行,结果如下:

1"use strict";
2
3require("core-js/modules/es.symbol.async-dispose.js");
4require("core-js/modules/es.symbol.dispose.js");
5// ... 更多es特性省略
6require("core-js/modules/esnext.array.filter-out.js");
7require("core-js/modules/esnext.array.filter-reject.js");
8// ... 更多esnext特性省略
9require("core-js/modules/web.dom-exception.stack.js");
10require("core-js/modules/web.immediate.js");
11// ... 更多web特性省略
12const jzplp = 1;
13

可以看到core-js被拆开,直接引入了特性本身。在配置chrome: 100版本时,引入的特性为215个。我们修改配置chrome: 140版本时,再重新生成代码,此时引入的特性为150个。可以看到确实时根据浏览器版本选择不同的特性引入。这对于其它core-js的引入方式也生效:

1// 源代码
2require('core-js/stable');
3const jzplp = 1;
4
5// 生成代码
6"use strict";
7
8require("core-js/modules/es.symbol.async-dispose.js");
9require("core-js/modules/es.symbol.dispose.js");
10// ... 更多es特性省略
11require("core-js/modules/web.dom-exception.stack.js");
12require("core-js/modules/web.immediate.js");
13// ... 更多web特性省略
14const jzplp = 1;
15

我们引入core-js/stable,可以看到生成代码中不引入esnext特性了。在配置chrome: 100版本时,引入的特性为71个,配置chrome: 100版本时,引入的特性为6个。同样的,如果引入换成core-js/full/array,就会只引入数组相关特性,而且也是根据浏览器兼容版本引入。

preset-env配置usage

@babel/preset-env的useBuiltIns配置值为usage时,Babel不仅会跟根据配置的浏览器版本兼容性,还会根据代码中实际使用的特性来选择引入哪些core-js中的特性。首先是Babel配置:

1{
2  "presets": [
3    [
4      "@babel/preset-env",
5      {
6        "targets": {
7          "chrome": "100"
8        },
9        "useBuiltIns": "usage",
10        "corejs": "3.47.0"
11      }
12    ]
13  ]
14}
15

然后是要处理的代码,注意配置usage时是不需要手动引入core-js的。我们配置不同的Chrome浏览器版本,看看输出结果如何:

1// 源代码
2const jzplp = new Promise();
3const b = new Map();
4
5// chrome 50 生成代码 
6"use strict";
7
8require("core-js/modules/es.array.iterator.js");
9require("core-js/modules/es.map.js");
10require("core-js/modules/es.promise.js");
11require("core-js/modules/web.dom-collections.iterator.js");
12const jzplp = new Promise();
13const b = new Map();
14
15// chrome 100 生成代码 
16"use strict";
17
18const jzplp = new Promise();
19const b = new Map();
20

首先可以看到,引入core-js中的特性数量变得非常少了,代码中没有用到的特性不再引入。其次不同的浏览器版本引入的特性不一样,因此还是会根据浏览器兼容性引入特性。我们再修改一下源代码试试:

1// 源代码
2const jzplp = new Promise();
3const b = new Map();
4Promise.try(() =>{});
5
6// chrome 50 生成代码 
7"use strict";
8
9require("core-js/modules/es.array.iterator.js");
10require("core-js/modules/es.map.js");
11require("core-js/modules/es.promise.js");
12require("core-js/modules/es.promise.try.js");
13require("core-js/modules/web.dom-collections.iterator.js");
14const jzplp = new Promise();
15const b = new Map();
16Promise.try(() => {});
17
18// chrome 100 生成代码 
19"use strict";
20
21require("core-js/modules/es.promise.try.js");
22const jzplp = new Promise();
23const b = new Map();
24Promise.try(() => {});
25

可以看到,源代码中增加了Promise.try,引入的特性也随之增加了对应的core-js特性引入。因此,使用@babel/preset-env的usage配置,可以保证兼容性的同时,最小化引入core-js特性。另外这个配置并不会自动引入提议特性,如果需要则额外配置proposals为true。

@babel/polyfill

@babel/polyfill是一个已经被弃用的包,推荐直接使用core-js/stable。查看@babel/polyfill源码,发现他就是引入了core-js特性与regenerator-runtime这个包。regenerator-runtime也是一个兼容性相关的包,可以帮助添加generatore和async/await相关语法。作为替代可以这样引入:

1import 'core-js/stable';
2import 'regenerator-runtime/runtime';
3

@babel/runtime

@babel/runtime就像自动引入版的core-js-pure。它还是根据代码实际使用的特性来注入core-js特性,但它不注入到全局,而是引入这些API再调用。这里我们使用@babel/plugin-transform-runtime插件,里面包含了@babel/runtime相关逻辑。首先看下Babel配置:

1{
2  "plugins": [["@babel/plugin-transform-runtime", { "corejs": 3 }]]
3}
4

再转义上一节中的代码,结果如下:

1// 源代码
2const jzplp = new Promise();
3const b = new Map();
4Promise.try(() =>{});
5
6// 生成代码
7import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
8import _Map from "@babel/runtime-corejs3/core-js-stable/map";
9const jzplp = new _Promise();
10const b = new _Map();
11_Promise.try(() => {});
12

可以看到,虽然没有直接引入core-js-pure,但效果是一样的。打开@babel/runtime-corejs3这个包查看,里面实际上就是导出了core-js-pure中的特性。例如:

1// @babel/runtime-corejs3/core-js-stable/map.js 文件内容
2module.exports = require("core-js-pure/stable/map");
3

core-js/configurator强制控制

如果希望在正常引入core-js时,对于部分特殊属性进行引入或者不引入的控制,就需要用到core-js/configurator。这个工具可以配置三种选项:

  • useNative: 当环境中有这个特性时不引入,当确定没有时才引入
  • usePolyfill: 明确引入这个特性
  • useFeatureDetection: 默认行为,和不使用core-js/configurator一致

useNative不引入

首先试试不引入特性,这里我们使用Promise这个特性为例。首先是不引入core-js的效果,可以看到全局Promise对象被我们改掉了。

1const jzplp = {};
2Promise = jzplp;
3console.log(Promise, Promise === jzplp);
4
5/* 输出结果
6{} true
7*/
8

然后在中间引入core-js试试。可以看到我们改掉的Promise,被core-js给改回去了。

1const jzplp = {};
2Promise = jzplp;
3require("core-js/actual");
4console.log(Promise, Promise === jzplp);
5
6/* 输出结果
7[Function: Promise] false
8*/
9

这时候,如果不希望core-js改掉我们自定义的Promise,可以利用useNative配置,强制core-js不引入这个特性。看结果core-js引入之后,我们自定义的Promise依然存在。

1const configurator = require("core-js/configurator");
2configurator({
3  useNative: ["Promise"],
4});
5const jzplp = {};
6Promise = jzplp;
7require("core-js/actual");
8console.log(Promise, Promise === jzplp);
9
10/* 输出结果
11{} true
12*/
13

usePolyfill强制引入

想要验证usePolyfill的效果,需要找一个环境中本来存在的特性,core-js即使引入也不会修改的特性。Promise不行,因为core-js引入时会对这个Promise增加子特性。Promise.try也不行,因为原来环境中不存在。这里试一下Promise.any,这是环境中本来就存在的特性:

1console.log(Promise.any);
2const jzplp = () => {};
3Promise.any = jzplp;
4console.log(Promise.any, Promise.any === jzplp);
5
6/* 输出结果
7[Function: any]
8[Function: jzplp] true
9*/
10

可以看到,Promise.any原来就存在,但是被我们修改成了新函数。再引入core-js试试:

1console.log(Promise.any);
2const jzplp = () => {};
3Promise.any = jzplp;
4require('core-js');
5console.log(Promise.any, Promise.any === jzplp);
6
7/* 输出结果
8[Function: any]
9[Function: jzplp] true
10*/
11

引入了core-js之后,结果没有变化。这说明core-js并不会修改我们自定义的函数。这时候就可以试一下usePolyfill的效果了:

1const configurator = require("core-js/configurator");
2configurator({
3  usePolyfill: ["Promise.any"],
4});
5console.log(Promise.any);
6const jzplp = () => {};
7Promise.any = jzplp;
8require('core-js');
9console.log(Promise.any, Promise.any === jzplp);
10
11/* 输出结果
12[Function: any]
13[Function: any] false
14*/
15

可以看到,Promise.any又被改为了真正起效果的函数,这说明usePolyfill的强制引入特性是有效的。

core-js中的特性选择

前面我们体验了Babel根据浏览器兼容性,选择不同的core-js特性引入,那么不同浏览器兼容哪些特性的数据是从哪里获取呢?core-js本身就提供了这个功能。

core-js-compat

core-js-compat提供了不同浏览器对应特性的兼容性数据。它有好几个参数,这里先列举一下含义:

  • targets: Browserslist格式的浏览器兼容配置
  • modules: 需要设置兼容性配置的模块,可以是core-js/full,也可以是某个特性,甚至是正则
  • exclude: 需要排除的模块
  • version: 使用的core-js版本
  • inverse: 反向输出,即输出不需要兼容的特性列表

这里举几个例子试一下:

1const compat = require("core-js-compat");
2const data = compat({
3  targets: "> 10%",
4  modules: ["core-js/actual"],
5  version: "3.47",
6});
7console.log(data);
8
9/* 输出结果
10{
11  list: [
12    'es.iterator.concat',
13    'es.math.sum-precise',
14    'es.async-iterator.async-dispose',
15    'esnext.array.group',
16    'esnext.array.group-by',
17    ...其它特性
18  ],
19  targets: {
20    'es.iterator.concat': { 'chrome-android': '143' },
21    'es.math.sum-precise': { 'chrome-android': '143' },
22    'es.async-iterator.async-dispose': { 'chrome-android': '143' },
23    'esnext.array.group': { 'chrome-android': '143' },
24    'esnext.array.group-by': { 'chrome-android': '143' },
25    ...其它特性
26  }
27}
28*/
29

compat会根据我们设置的浏览器兼容性配置,输出特性列表,包含两个字段:list是一个特性名称列表;targets是一个Map结构,key为特性名,值为可以兼容的浏览器。假设我们把上面的 targets改成 > 50%,此时会输出空值:

1const compat = require("core-js-compat");
2const data = compat({
3  targets: "> 50%",
4  modules: ["core-js/actual"],
5  version: "3.47",
6});
7console.log(data);
8
9/* 输出结果
10{ list: [], targets: {} }
11*/
12

我们增加exclude,排除部分属性,可以看到特性数量大大减少:

1const compat = require("core-js-compat");
2const data = compat({
3  targets: "> 10%",
4  modules: ["core-js/actual"],
5  exclude: ["esnext"],
6  version: "3.47",
7});
8console.log(data);
9
10/* 输出结果
11{
12  list: [
13    'es.iterator.concat',
14    'es.math.sum-precise',
15    'es.async-iterator.async-dispose',
16    'web.dom-exception.stack',
17    'web.immediate',
18    'web.structured-clone'
19  ],
20  targets: {
21    'es.iterator.concat': { 'chrome-android': '143' },
22    'es.math.sum-precise': { 'chrome-android': '143' },
23    'es.async-iterator.async-dispose': { 'chrome-android': '143' },
24    'web.dom-exception.stack': { 'chrome-android': '143' },
25    'web.immediate': { 'chrome-android': '143' },
26    'web.structured-clone': { 'chrome-android': '143' }
27  }
28}
29*/
30

再试一下inverse的效果:

1const compat = require("core-js-compat");
2const data = compat({
3  targets: "> 10%",
4  modules: ["core-js/actual"],
5  version: "3.47",
6  inverse: true
7});
8console.log(data);
9
10/* 输出结果
11{
12  list: [
13    'es.symbol',
14    'es.symbol.description',
15    ...其它特性
16  ],
17  targets: {
18    'es.symbol': {},
19    'es.symbol.description': {},
20    ...其它特性
21  }
22}
23*/
24

因为输出的是不需要引入core-js兼容的特性,所以特性数量非常多,而且targets中没有列出支持的浏览器版本。

core-js-builder

前面介绍的core-js-compat是接收参数之后,输出core-js的特性列表数组。而core-js-builder接收类似的参数,直接输出引用core-js的代码。我们首先列举一下参数:

  • targets: Browserslist格式的浏览器兼容配置
  • modules: 需要设置兼容性配置的模块,可以是core-js/full,也可以是某个特性,甚至是正则
  • exclude: 需要排除的模块
  • format: 'bundle'输出打包后的源码;'cjs'和'esm'输出对应格式的引用代码
  • filename: 输出的文件名

我们先试一下例子。首先是format格式的:

1const builder = require("core-js-builder");
2async function funJzplp() {
3  const data = await builder({
4    targets: "> 30%",
5    modules: ["core-js/actual"],
6    format: 'bundle',
7  });
8  console.log(data);
9}
10funJzplp();
11
12/* 输出结果
13...代码很长,这里节选部分
14 (function(module, exports, __webpack_require__) {
15"use strict";
16var NATIVE_BIND = __webpack_require__(8);
17var FunctionPrototype = Function.prototype;
18*/
19

可以看到,builder函数输出了非常长的代码,内容实际为输出的特性经过打包之后的结果代码。再试一下'cjs'和'esm',输出的是对应木块的引用代码:

1// format: 'cjs' 输出结果
2...代码很长,这里节选部分
3require('core-js/modules/es.iterator.concat');
4require('core-js/modules/es.math.sum-precise');
5require('core-js/modules/es.async-iterator.async-dispose');
6*/
7
8// format: 'esm' 输出结果
9...代码很长,这里节选部分
10import 'core-js/modules/es.iterator.concat.js';
11import 'core-js/modules/es.math.sum-precise.js';
12import 'core-js/modules/es.async-iterator.async-dispose.js';
13*/
14

如果设置了filename,core-js-builder会创建该名称的文件,并将代码写入到文件中。

总结

这篇文章描述了core-js相关包的代码内容和使用方式。core-js实际上就是提供了JavaScript中一些API特性的兼容实现方式。它与实现语法兼容的Babel一起,可以做到大部分JavaScript的兼容性。当然core-js和Babel也不是万能的,它们都有各自无法转义和兼容的语法和特性。

core-js这个包名字起的非常好,一听就是JavaScript的“核心”包。由于它实现了很多API特性,而且引用数量非常非常大,因此叫“核心”也不为过。虽然这个包引用量很大,但不如React/Vue或者一些其它包出名。因为这个包是处理的更底层的兼容性有问题,因此用户感知不强。core-js包的作者还因为没有钱而遇到很多问题,这个包并没有让他变的富有。

参考


Polyfill方式解决前端兼容性问题:core-js包结构与各种配置策略》 是转载文章,点击查看原文


相关推荐


一文搞懂机器学习中的特征降维!
aicoting2026/1/12

推荐直接网站在线阅读:aicoting AI算法面试学习在线网站 特征工程(Feature Engineering) 是机器学习流程中将原始数据转换为适合模型学习的特征的关键步骤。它直接决定了模型能否高效捕捉数据中的规律。好的特征可以显著提升模型性能,而差的特征即使模型再复杂也难以取得好效果。 特征工程的核心目标是: 提取有效信息:将原始数据中有价值的信号转化为模型可以理解的特征; 减少冗余与噪声:去掉无关或多余的特征,使模型更简洁、更泛化; 增强表达能力:通过构造、组合或降维生成新的特征,


Day 12:Git配置详解:用户信息、编辑器、颜色等配置
CNRio2026/1/4

“你有没有遇到过这样的尴尬:提交代码时,Git显示’Author: Unknown’,然后你发现是自己写的代码,却不知道是谁提交的?别担心,这就像你写了一封信,却没写署名一样!” 🌟 为什么说Git配置是"代码身份证"? 想象一下,你正在写一本小说,每章都署名"匿名作者"。读者会怎么想?他们可能会怀疑这本书是不是真的由你写的。Git配置就是你的"代码身份证",它告诉世界"这代码是我写的"。 正如《Pro Git》中所说: “Git的配置系统是分层的,有三个层次:系统级、全局级和本地级。系统


LeetCode 热题100 --- 双指针专区
谎言西西里2025/12/26

283. 移动零 - 力扣(LeetCode) 题目分析: 题目要求将数组 nums 中所有 0 移动至数组末尾,同时保持其他非零元素的相对顺序不变,并且要求在原数组上进行操作。 核心要求: 0 要移动至数组末尾 非零元素相对位置不变 在原数组上进行操作 解法一(暴力使用数组方法) 遍历数组将其中所有为 0 的数直接使用splice删除并且记录 0 的个数,最后通过push填入“移动”的 0 var moveZeroes = function(nums) { let n = 0;


【大前端】【Android】 Android 手机上导出已安装 App 的 APK
柯南二号2025/12/17

根据是否有 root / adb / 仅手机操作,常见有 4 种靠谱方式。按「实用度 + 成本」整理👇 一、最推荐:ADB 导出(无需 Root,最稳定)⭐️ 适合开发者、抓包、逆向、分析三方 APK 1️⃣ 开启 USB 调试 设置 → 关于手机 → 连续点击“版本号” → 开发者模式 开发者选项 → USB 调试 2️⃣ 找到 APK 路径 adb shell pm list packages | grep wechat 例如: package:com.tence


Agent 入门科普:从"人工智障"到"数字打工人"的进化史
无限大62025/12/9

🤖 Agent 入门科普:从"人工智障"到"数字打工人"的进化史 大家好,欢迎来到无限大的博客,这个专栏是新开的,打算讲一讲Agent,其实早就有学习的打算了 近期在逛github的时候看到一个高star项目,叫做Hello-Agents,项目地址是[github.com/datawhalech…] 我的文章也是参考了这个内容写的,这个系列更新比较慢,因为我也是边学边写的,所以会比较慢,但是我会尽量写的详细一些,用更多贴近生活的抽象案例来讲解,希望能帮助到大家 引言:当 AI 开始自己"打


浅谈C++与C语言二进制文件差异(从一次链接错误说起)
码事漫谈2025/11/29

"undefined reference to `func' ",这个看似简单的链接错误背后,隐藏着C与C++二进制文件的根本差异。很多开发者认为C++只是"C with Classes",却不知这对"亲密兄弟"在二进制层面早已分道扬镳。 在软件开发的演进历程中,C++作为C语言的延伸,始终保持着高度的语法兼容性。这种表面上的相似性却掩盖了两者在编译产物层面的深刻差异。本文将从二进制文件的视角,深入剖析C++与C语言在目标代码生成机制上的本质区别,揭示面向对象、泛型编程等高级特性在机器层面的实现

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客