Adamwu1992 / adamwu1992.github.io

My Blog
2 stars 0 forks source link

【babel】按需引入polyfill #9

Open Adamwu1992 opened 6 years ago

Adamwu1992 commented 6 years ago

babel-preset-env是我们现在常用的babel转换插件,相较于它的前任babel-preset-latest,它有更加丰富的配置接口来减少我们的代码体积。

背景

如果我们在项目用用到了es5+的新特性,又要项目支持版本较低的浏览器,我们通常会引入babel-polyfill来转换一些特定的对象,比如Symbol之类的,如果是用import 'babel-polyfill'这种方式,会将整个polyfill打包到我们的代码中,哪怕我们仅仅是用到了一个特性而已,这是一种浪费,有没有什么方法可以做到按需引入呢?

core-js

core-js是一个用户JavaScript的模块化标准库,babel-polyfill就是基于它提供了一个完成的ES5+的环境。

传统的使用babel-polyfill的方式是在类似entry这样的入口文件里引用,以保证polyfill的代码在所有其他代码之前生效,但是要做到最严格的按需引入,最直接的方法就是,一个文件里用到了哪些特性,就在文件头部引入哪些特性,为了防止某一个特性被重复引入,大约有两种办法:

而且更加难处理的就是,你不知道某些特性原生浏览是否已经实现了,如果浏览器实现了的话再引入polyfill就显得比较多余了。

targets && useBuiltIns

退而求其次,我们用babel-preset-env提供的一个更加自动化的方法来实现。

为什么是退而求其次呢?因为这个还算不上严格意义上的按需引入。首先babel-preset-env有一个配置项targets,它接受一个对象,用于配置需要支持的运行环境,我们可以在这里配置{"chrome": 52},表示最低只需要支持到Chrome的52.0.0版本,配合兼容性表就可以得知某个特性是否需要polyfill,事实上,babel是通过定期运行build-data.js来生成plugins.json的。

还有最重要的一点就是useBuiltIns选项,这个选项是一个Boolean,默认是false,也就是什么都不会发生。如果配置成true,那么babel就会替换掉我们的import 'babel-polyfill'或者require('babel-polyfill'),替换成什么呢?

require('core-js/modules/web.timers');
require('core-js/modules/web.immediate');
require('core-js/modules/web.dom.iterable');

以上的代码是我将babel的targets配置为最低支持到Chrome 68生成的代码,说明babel-polyfill中其他的特性浏览器都实现了。

babel会自动引入我们浏览还未实现的特性,但是不会管你代码里到底有没有用到这些,所以说不是严格意义上的按需引入。

未来已来

讲了半天按需引入,如隔靴搔痒,很不痛快。 但是最近babel7的出现了,也带来了新的@babel/preset-env,新的useBuiltIns支持"usage"|"entry"|false

不配置(默认false)配置成false还是和以前一样什么都不发生。

配置成"entry",和babel@6.x时配置成"true"的表现一样。

最骚的是"usage",首先,不需要在业务代码里手动引入babel-polyfill,babel会根据targets支持情况,结合你代码里使用到的特性,自动在打包后的代码里按需引入,一下是我配置支持到IE 8的代码:

const a = [1, 2, 3, 4, 5];
const b = a.map(n => n * 10);

const c = Symbol();

to

"use strict";

require("core-js/modules/es7.symbol.async-iterator");

require("core-js/modules/es6.symbol");

require("core-js/modules/es6.array.map");

var a = [1, 2, 3, 4, 5];
var b = a.map(function (n) {
  return n * 10;
});
var c = Symbol();

非常完美,就是不太稳定,截止到目前还是beta.51版本,耐心等下吧。

emma-dan-guo commented 4 years ago

里面具体「新的useBuiltIns支持"usage"|"entry"|false。」这里面按需加载的是怎么实现的呀