关于Babel你只需要知道三个插件

文章总结的时间是2017/11/20

本文是为了梳理Babel配置及使用而整理,因为看过使用Babel配置项目和文章,存在项目插件使用混乱、文章各种照搬、插件使用听风是雨、插件升级文章内容不再适用的问题。这里就目前最新使用的配置组合进行整理,涉及的插件包括以下三个:

  • @babel/preset-env(^7.0.0-beta.32)
  • @babel/preset-stage-x(7.0.0-beta.32), x-0,1,2,3
  • @babel/polyfill(^7.0.0-beta.32)

@babel/preset-env

特性

替换之前所有babel-presets-es20xx插件

A Babel preset that compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.

也就是说,这是一个能根据运行环境为代码做相应的编译,@babel/preset-env的推出是为了解解决个性化输出目标代码的问题,通过browserslist语法解析需要支持的目标环境,根据环境将源代码转义到目标代码,可以实现代码精准适配。

此外,@babel/preset-env不包含state-x一些列插件,只支持最新推出版本的JavaScript语法(state-4),关于state-x后面会介绍。

更进一步说明,请参考Dr. Axel Rauschmayer的介绍。这个插件对特殊平台的开发有很大帮助,比如:Electron、大屏、移动端(只考虑webkit)等。

替换@babel/plugin-transform-runtime的使用

@babel/plugin-transform-runtime插件是为了解决:

  • 多个文件重复引用相同helpers(帮助函数)-> 提取运行时
  • 新API方法全局污染 -> 局部引入

这个插件推荐在编写library/module时使用。当然,以上问题可通过设置useBuiltIns搞定。

支持实例方法按需引入

传统方式是手动从core-js引入需要的ES6+特性,

require('core-js/fn/set');
require('core-js/fn/array/from');
require('core-js/fn/array/find-index');
...
...

或者一股脑全部引入:

import '@babel/polyfill'
// or
require('@babel/polyfill')

同样,以上问题可通过设置useBuiltIns搞定。

使用说明

默认情况下,@babel/preset-env的效果和@babel/preset-latest一样,虽然上面的说明有提到polyfills,但是也需要在配置中设置useBuiltIns才会生效。

主要配置

targets

设置支持环境,支持的key包括:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron。

例如:

{
  "presets": [
    ["@babel/env", {
      "targets": {
        "node": "current",
        "chrome": 52,
        "browsers": ["last 2 versions", "safari 7"]
      }
    }]
  ]
}

其中,browserslist可在package.json中配置,这个和设置CSS的Autoprefixer一致,配置优先级如下:

targets.browsers > package.json/browserslist

此外,browserslist的配置满足最大匹配原则,比如需要同时支持在 IE 8 和 Chrome 55 下运行,则preset-env提供所有 IE 8 下需要的插件,即使 Chrome 55 可能不需要。

modules

选项用于模块转化规则设置,可选配置包括:"amd" | "umd" | "systemjs" | "commonjs" | false, 默认使用 "commonjs"。即,将代码中的ES6的import转为require

如果你当前的webpack构建环境是2.x/3.x,推荐将modules设置为false,即交由 Webpack 来处理模块化,通过其 TreeShaking 特性将有效减少打包出来的 JS 文件大小。这部分参考这里的回答:ECMAScript 6 的模块相比 CommonJS 的require (...)有什么优点?

useBuiltIns

A way to apply @babel/preset-env for polyfills (via @babel/polyfill).

可选值包括:"usage" | "entry" | false, 默认为 false,表示不对 polyfills 处理,这个配置是引入 polyfills 的关键

"useBuiltIns":"usage"

在文件需要的位置单独按需引入,可以保证在每个bundler中只引入一份。例如:

In

a.js

var a = new Promise();

b.js

var b = new Map();

Out (if environment doesn't support it)

import "core-js/modules/es6.promise";
var a = new Promise();
import "core-js/modules/es6.map";
var b = new Map();

Out (if environment supports it)

var a = new Promise();
var b = new Map();

!!注意!!

当前模式类似于@babel/plugin-transform-runtime,polyfill局部使用,制造一个沙盒环境,不造成全局污染,但是如上配置后,@babel/preset-env能按需引入新实例方法,例如:

"foobar".includes("foo")

@babel/plugin-transform-runtime不行,需要自行从core-js中按需引入。我测试的情况和下面这篇文章所说完全不一致。

原文:https://zhuanlan.zhihu.com/p/29506685

当 useBuiltIns 设置为 usage 时,Babel 会在你使用到 ES2015+ 新特性时,自动添加 babel-polyfill 的引用,并且是 partial 级别的引用。

请注意: usage 的行为类似 babel-transform-runtime,不会造成全局污染,因此也会不会对类似 Array.prototype.includes() 进行 polyfill

"useBuiltIns":"entry"

在项目入口引入一次(多次引入会报错)

import "@babel/polyfill"
// or
require("@babel/polyfill")

插件@babel/preset-env会将把@babel/polyfill根据实际需求打散,只留下必须的,例如:

In

import "@babel/polyfill";

Out (different based on environment)

import "core-js/modules/es6.promise";
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
import "core-js/modules/es7.array.includes";

除了新API,也可以是实例方法,不过最终包体积比使用"useBuiltIns":"usage"大30kb左右(因项目而异)。

"useBuiltIns": false

不在代码中使用polyfills,表现形式和@babel/preset-latest一样,当使用ES6+语法及API时,在不支持的环境下会报错。

@babel/preset-stage-x

这里需要说明下ES特性支持的提案,不同阶段的提案支持的内容不同,其中stage-4阶段提案中的特性将会在未来发布。

关于各个Stage的说明参考这里。上面介绍的@babel/preset-env或者@babel/preset-latest就是下面提到的stage-4

The TC39 categorizes proposals into the following stages:

  • Stage 0 - Strawman: just an idea, possible Babel plugin.
  • Stage 1 - Proposal: this is worth working on.
  • Stage 2 - Draft: initial spec.
  • Stage 3 - Candidate: complete spec and initial browser implementations.
  • Stage 4 - Finished: will be added to the next yearly release.

Stage的包含顺序是:左边包含右边全部特性,即stage-0包含右边 1 / 2 / 3 的所有插件。

stage-0 > ~1 > ~2 > ~3 > ~4: 

疑问

1. 这个和@babel/preset-env的区别

@babel/preset-env会根据预设的浏览器兼容列表从stage-4选取必须的plugin,也就是说,不引入别的stage-x@babel/preset-env将只支持到stage-4

建议

1. 如果是React用户,建议配到@babel/preset-stage-0

其中的两个插件对于写JSX很有帮助。

  • transform-do-expressions:if/else三目运算展开
  • transform-function-bind:this绑定

2. 通常使用建议配到@babel/preset-stage-2

插件包括:

  • syntax-dynamic-import: 动态import
  • transform-class-properties:用于 class 的属性转化
  • transform-object-rest-spread:用来处理 rest spread
  • transform-async-generator-functions:用来处理 async 和 await

@babel/polyfill

这个插件是对core-jsregenerator-runtime的再次封装,在@babel/preset-env中的useBuiltIns: entry用到,代码不多

if (global._babelPolyfill) {
  throw new Error("only one instance of @babel/polyfill is allowed");
}
global._babelPolyfill = true;

import "core-js/shim";
import "regenerator-runtime/runtime";

core-js/shim

shim only: Only includes the standard methods.

只包含了纳入标准的API实例化方法,例如下列常见的。注意,这里没有generator/async,如果需要,那就安装插件@babel/preset-stage-3(或者0 / 1 / 2)

...
require('./modules/es6.string.trim');
require('./modules/es6.string.includes');
require('./modules/es7.array.includes');
require('./modules/es6.promise');
...

regenerator-runtime/runtime

Standalone runtime for Regenerator-compiled generator and async functions.

主要是给generator/async做支持的插件。

总结

这里需要理解下三个概念:

  • 最新ES 语法:比如,箭头函数
  • 最新ES API:,比如,Promise
  • 最新ES 实例方法:比如,String.protorype.includes

@babel/preset-env默认支持语法转化,需要开启useBuiltIns配置才能转化API实例方法

此外,不管是写项目还是写Library/Module,使用@babel/preset-env并正确配置就行。多看英文原稿说明,中文总结看看就好,别太当真。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,827评论 1 317
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,511评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,318评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,108评论 0 189
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,112评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,387评论 1 185
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,905评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,657评论 0 177
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,144评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,744评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,100评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,565评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,041评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,769评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,301评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,173评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,313评论 2 242

推荐阅读更多精彩内容