在对babel进行配置的时候,我们一般都是复制粘贴官网上例子或者使用现成的脚手架,但其实其中的每一项我们都不太了解,它是做什么的?实现了什么功能?因此,做一下具体详细的记录。
1.首先es6增加的内容分为语法和api
语法:比如箭头函数和解构
const fn = () => {}
const arr2 = [...arr1]
新的api:比如map和promise
const m = new Map()
const p = new Promise(() => {})
2.@babel/core
安装:npm install @babel/core --save-dev
@babel/core是babel的核心,它的作用是按照配置的文件进行转码。
3.@babel-cli
安装:npm install @babel/cli
@babel/cli的作用是让我们能够在命令行使用babel功能
4.plugins和presets
官网:babel是基于插件架构的,如果不引入插件,babel不会做任何事情,输入什么就输出什么。我们如果想转换箭头函数,直接引用箭头函数插件即可。
/* .babelrc */
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
如果想引入转换解构的插件:
/* .babelrc */
{
"plugins": [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-destructuring"
]
}
但是es6的新增语法那么多,这样引入的插件也太多了啊,幸亏babel提供了插件的集合presets,它相当于插件的集合,比如:preset-env(专门处理es6+规范语法的集合) preset-stage(处理尚在提案时期的语法集合) preset-react(处理react语法的集合)
5.preset-env
/* .babelrc */
{
"presets": ["@babel/preset-env"]
}
preset-env可以让你尽情使用es6的语法,它会帮你转换成浏览器能够识别的js语法,默认情况下,不用配置任何东西,它会转换所有的es6+的语法,我们可以给他加一个targets用来给他设定转换的浏览器版本。
/* .babelrc */
{
"presets": [
["@babel/preset-env", {
"targets": "ie >= 8"
}]
]
}
只有ie浏览器版本大于8的时候,才会转换es6+的语法,其他不予转换。
6.@babel/polyfill
当我们引入promise的时候,发现转换过后的文件并没有发生任何变化,这是因为es6的语法部分可以用@babel/preset-env进行转换,但是api部分,如map 和 promise 等要用到ployfill进行转换。
/* test.js */
import '@babel/polyfill'
const fn = () => {}
new Promise(() => {})
/* test-compiled.js */
import '@babel/polyfill';
var fn = function fn() {};
new Promise(function () {});
只需要在index.js文件中直接用import引入即可。这样,使用promise的文件就可以运行在ie8环境中了,但是我们发现,引入这个包之后,文件的体积变大了好几倍。这是因为,polyfill在处理浏览器不支持的属性的时候,它会自己实现一遍所有的es6+ API,为了让它只引入我们使用的api,preset-env帮助我们实现了这个功能:
/* .babelrc */
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "entry",
"core-js":3,//需要声明core-js的版本,否则webapck会报错
"targets": "ie >= 8"
}]
]
}
useBuiltIns属性有两个值,entry 和 usage和false。entry的作用是在程序的入口处,将ie8所有不支持的api的polyfill都引入进来。能够覆盖到‘hello‘.includes(‘h‘)这种句法,足够安全且代码体积不是特别大!(推荐使用)
/* test.js */
import '@babel/polyfill'
const fn = () => {}
new Promise(() => {})
/* test-compiled.js */
import "core-js/modules/es6.array.copy-within";
import "core-js/modules/es6.array.every";
import "core-js/modules/es6.array.fill";
... //省略若干引入
import "core-js/modules/web.immediate";
import "core-js/modules/web.dom.iterable";
import "regenerator-runtime/runtime";
var fn = function fn() {};
new Promise(function () {});
usage更加智能一些,它会扫描你的代码,你的代码用到了哪个api,就会引入相应的polyfill。但是,我们在转译的时候都会除去node-modules,如果有第三方包没有做好es6转译的情况下,就会出问题。
但是它检测不到‘hello‘.includes(‘h‘)这种句法,可以在书写规范并且信任第三方包的情况下使用。
/* .babelrc */
{
"presets": [
["@babel/preset-env", {
"modules": false,
"useBuiltIns": "usage",
"core-js":2,//也因 core-js@3 的原因,需要配置 corejs 参数去指定使用的corejs 版本,否则 webpack 运行时会报 warning。
"targets": "ie >= 8"
}]
]
}
/* test.js */
const fn = () => {}
new Promise(() => {})
/* test-compiled.js */
import "core-js/modules/es6.promise";
import "core-js/modules/es6.object.to-string";
var fn = function fn() {};
new Promise(function () {});
useBuiltIns为false时,会默认把所有的包引入,体积较大。不推荐使用。
注:为什么无法识别includes等api?
usage时通过识别api来引入垫片,比如promise、map等,但是includes是在数组原型上的方法(Array.prototype
.includes),但是无法确认引用includes方法的是什么类型,所以无法引入数组原型。
如果你在写一个APP或者页面以上配置已经足够了,但是如果你想写一个类库或者框架,还有一个需要注意的地方。
以上使用polyfill的方法会污染全局环境,可能会覆盖掉一些全局的api。我们需要换一种方法来引入polyfill:
安装:yarn add @babel/plugin-transform-runtime -D
yarn add @babel/runtime-corejs2
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2 // 推荐
}
]
]
}
/* test.js */
class Test {}
new Promise(() => {})
/* test-compiled.js */
import _Promise from "@babel/runtime-corejs2/core-js/promise";
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";
var Test = function Test() {
_classCallCheck(this, Test);
};
new _Promise(function () {});
接下来,我们记录一下这两种方法的区别,首先,这两种方法都可以实现es6 API 的转换:
1.@babel/preset-env + @babel/polyfill可以转译语法、新 API,但存在污染全局问题,用在业务场景
2.@babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs2,可按需导入,转译语法、新 API,且避免全局污染(会为引入的模块创建一个沙箱环境),但是检测不到‘hello‘.includes(‘h‘)这种句法;
**core-js3改进了core-js2不能识别原型方法的这个特点,就includes方法来说,因为只有数组和字符串可以用这个方法,所以会一次性将数组和字符串的原型都引入。**建议升级使用core-js3
借鉴:
https://segmentfault.com/a/1190000020237817
https://zhuanlan.zhihu.com/p/139359864
https://segmentfault.com/a/1190000018721165