babel实现polyfill的几种方式以及对项目包大小的影响

682 阅读3分钟

@babel/preset-env的polyfill功能浅析

关于这个babel preset不做过多介绍, 想必大家都很熟悉。这片文章主要介绍这个preset是如何支持polyfill的。(想必polyfill也不用我多介绍了吧 - - )

在babel 7.4之前,如果我们想在浏览器环境下用到一些ES的新功能,例如async/await, 需要在代码一开始引入@babel/polyfill这个库,实现async/await的功能。在babel7.4之后,我们可以通过配置@babel/preset-env来达到polyfill的功能。这个配置项就是useBuiltIn。官网链接:babeljs.io/docs/en/bab… 下面对它的各项取值,结合例子来详细说明。

1. useBuiltsIn: entry

此种方式,会将代码中显式引入的core-js在编译阶段,根据配置的环境,动态替换成特定的core-js entry入口。是不是比较抽象?我们用例子来说明,对promise来做polyfill。

//webpack.config.js
 module: {
        rules: [
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                  presets: [['@babel/preset-env', {
                    "targets": {
                        "chrome": "30"
                      },
                      "useBuiltIns": "entry"
                }]]
              }
            }
          }
        ]
      }
// source code
import "core-js";
const main = new Promise((res, rej) =>
{
    console.log(res);
});

export { main };

代码完成,运行一下。结果控制台报错了~ ERROR in ./src/index.js 113:0-45 Module not found: Error: Can't resolve 'core-js/modules/es6.weak-map.js' in '/demo/src'

类似的错误报了一大堆,原因就在于我们用的core-js版本不正确。需要在babel的配置中,显式的制定使用3.0以上的版本。好,修改配置:

//webpack.config.js
module: {
        rules: [
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                  presets: [['@babel/preset-env', {
                    "targets": {
                        "chrome": "30"
                      },
                      "useBuiltIns": "entry",
                      "corejs": "3.19",
                }]]
              }
            }
          }
        ]
      }

本次新增了corejs这个选项,配置为3.19的core-js版本,再来运行。如我们所料,这次编译很顺利,在/dist目录下多出了main.js文件。查看它的内容,可以发现已经将core-js的很多内容都打到了我们的main.js中。观察结果我们发现,main.js文件有1.3MB,其中涉及到引入core-js的有400多条。这也和官网的解释一致:“Importing "core-js" loads polyfills for every possible ECMAScript feature”。

再次做个实验,如果我们把babel配置项中的targets 变高一些呢?。如下:

"targets": {
    "chrome": "60"
},

再来打包看结果:这次打出来的main.js只有900多K。确实符合我们的预期:环境变了后,babel会智能的引入该环境下的polyfill函数。查看main.js的内容,关于core-js的引入条目减少到了200多条。

是不是个进步?

2.useBuiltsIns: usage

我们接下来看这个配置项。将targets恢复到之前的chrome > 30配置,将babel的useBuiltsIn改为usage:

"targets": {
    "chrome": "30"
},
"useBuiltIns": "usage",

此外,我们还需要修改源代码。因为这个选项不需要我们显式的引入core-js:

// source code
 // import "core-js"; 注释掉core-js的引入
const main = new Promise((res, rej) =>
{
    console.log(res);
});

export { main };

再次启动打包,发现这次的main.js大小只有385K,是不是远远小于 useBuiltsIn为entry时候的代码块体积?查看main.js的内容, 引入的core-js中的模块只有97条。

再进一步,将targets改为chrome > 60呢? 本次打包,main.js大小为382K,相比较之前,只少了3K, 效果不明显。main.js引入的有关core-js模块是95条。

"targets": {
    "chrome": "60"
},

感兴趣的童鞋可以再去试试。随着targets的配置越来越接近现代浏览器,打出的main.js包会越来越小哦~

@babel/plugin-transform-runtime

当然,还有一个plugin也能实现polyfill的功能,就是@babel/plugin-transform-runtime。这个plugin主要是用于减少打包后的bundle体积,以及避免全局污染。有兴趣的童鞋可以到官网了解下:babeljs.io/docs/en/bab…