babel-preset-es2015
是能将 es2015
新特性转换成 es3
的 babel plugin
的集合,这些集合到底包含了哪些插件呢,这篇博客会一一列出。
通过源码,可以直接看到引入了哪些插件,共计 24 个
- babel-plugin-transform-es2015-template-literals
- babel-plugin-transform-es2015-literals
- babel-plugin-transform-es2015-function-name
- babel-plugin-transform-es2015-arrow-functions
- babel-plugin-transform-es2015-block-scoped-functions
- babel-plugin-transform-es2015-classes
- babel-plugin-transform-es2015-object-super
- babel-plugin-transform-es2015-shorthand-properties
- babel-plugin-transform-es2015-duplicate-keys
- babel-plugin-transform-es2015-computed-properties
- babel-plugin-transform-es2015-for-of
- babel-plugin-transform-es2015-sticky-regex
- babel-plugin-transform-es2015-unicode-regex
- babel-plugin-check-es2015-constants
- babel-plugin-transform-es2015-spread
- babel-plugin-transform-es2015-parameters
- babel-plugin-transform-es2015-destructuring
- babel-plugin-transform-es2015-block-scoping
- babel-plugin-transform-es2015-typeof-symbol
- babel-plugin-transform-es2015-modules-commonjs
- babel-plugin-transform-es2015-modules-systemjs
- babel-plugin-transform-es2015-modules-amd
- babel-plugin-transform-es2015-modules-umd
- babel-plugin-transform-regenerator
这是 babel-preset-es2015@6.24.1
版本所引入的源码,如果使用 babel@7.x
,这些插件也还是存在,但名字去掉了 es2015
。下面以我认为的重要程度一一了解
babel-plugin-transform-arrow-functions
箭头函数转换,不是单纯将箭头语法做了转换,还会处理 this
值。
1 | const foo = () => {}; |
编译成
1 | var _this = this; |
箭头函数内部的 this
值会被替换,保证了 this
指向不改变。
如果有人问,箭头函数使用 .call
会改变 this
指向吗?答案是不会。
babel-plugin-transform-block-scoping
使用 {}
可以形成块级作用域,块级作用域的目的是为了不影响外部作用域。
1 | let foo = 'foo'; |
块级作用域外部 foo
仍然是 foo
,内部 foo
变成了 _foo
,保证了不发生覆盖。
1 | var foo = 'foo'; |
编译后的代码似乎有问题,bar
仍然可以在外部访问到。这是因为外部作用域没有使用到 bar
,如果代码是
1 | let foo = 'foo'; |
结果就和预期一致了,块级作用域内部的 bar
变量也被转换为 _bar
。
1 | var foo = 'foo'; |
并且该插件还用于 const
、let
关键字转化为 var
,但在 babel@6.x
时,需要配合 check-es2015-constants
插件才能正确处理 const
关键字重新声明。而在 babel@7.x
就不需要 check-es2015-constants
插件了。
但两者还是有一些区别,babel@6.x
会在编译时报错,而 babel@7.x
并不会报错,而是编译成
1 | function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } |
运行时才会报错。
babel-plugin-transform-spread
展开运算符,但在 es2015
规范中,只能用于展开数组,对象的展开还处于草案阶段,需要使用 babel-plugin-transform-object-rest-spread
插件。
1 | // 数组展开 |
这里有四个语法,但只有数组展开能被该插件处理,编译成
1 | // 数组展开 |
babel-plugin-transform-destructuring
解构语法支持,日常开发中最常用的语法,同样是上面的例子,能够正确处理数组收集和对象收集。
1 | function _objectWithoutProperties(source, excluded) { |
但是对象展开语法需要 @babel/plugin-proposal-object-rest-spread
支持。
还有个 @babel/plugin-syntax-object-rest-spread 插件,名字看起来差不多,但该插件无法处理对象展开。查看源码发现 proposal-object-rest-spread 引用了 syntax-object-rest-spread。
babel-plugin-transform-computed-properties
对象属性支持变量
1 | const foo = 'a'; |
编译成
1 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } |
babel-plugin-transform-shorthand-properties
对象属性简写
1 | const name = 'Hello'; |
编译成
1 | const name = 'Hello'; |
babel-plugin-transform-classes
类语法,更简单的方式写继承
1 | class Animal { |
编译成
1 | function _possibleConstructorReturn(self, call) { |
babel-plugin-transform-parameters
函数参数支持默认值、收集运算符
1 | function foo(a, b = 'b', { c }, ...rest) { |
编译成
1 | function foo(a) { |
可以发现,只有结构语法不被转换,加上 @babel/plugin-transform-destructuring
插件后,编译成
1 | function foo(a) { |
babel-plugin-transform-template-literals
模板字符串
1 | const foo = 'world'; |
编译成
1 | const foo = 'world'; |
babel-plugin-transform-modules-xxx
将 export default
这种语法(怎么称呼?标准模块机制?)转换为其他模块化代码
1 | import foo from './foo'; |
amd
1 | define(['exports', './foo'], function (exports, _foo) { |
umd
1 | (function (global, factory) { |
commonjs
1 | ; |
systemjs
1 | System.register(['./foo'], function (_export, _context) { |
babel-plugin-transform-regenerator
支持 generator
语法
1 | function* foo() { |
编译成
1 | var _marked = /*#__PURE__*/regeneratorRuntime.mark(foo); |
babel-plugin-transform-for-of
这里开始是一些比较少用的语法
1 | const obj = { |
编译成
1 | const obj = { |
babel-plugin-transform-object-super
支持调用超类
1 | const foo = { |
编译成
1 | var _get = function get(object, property, receiver) { |
babel-plugin-transform-duplicate-keys
支持写重复的键,忘记了在旧版本中如果对象有重复的键会怎么样了,但使用 eslint
的情况下,有重复键会报错的。
1 | const obj = { |
编译成
1 | const obj = { |
obj.a === 'c'
,支持重复的 key
有什么意义呢?
babel-plugin-transform-sticky-regex
1 | const regexp = /foo/y; |
编译成
1 | const regexp = new RegExp("foo", "y"); |
babel-plugin-transform-unicode-regex
1 | const string = "foo💩bar"; |
编译成
1 | const string = "foo💩bar"; |
babel-plugin-transform-typeof-symbol
1 | typeof Symbol() === 'symbol'; |
编译成
1 | var _typeof = typeof Symbol === "function" |
babel-plugin-transform-literals
编码转换
1 | const u = 'Hello\u{000A}\u{0009}!'; |
编译成
1 | const u = 'Hello\n\t!'; |
babel-plugin-transform-block-scoped-functions
从名字来看,是块级作用域函数,是指什么呢?
是让块级作用域内声明的函数只作用域块级作用域。
1 | { |
编译成
1 | { |
感觉和 block-scoping
有点类似,块级作用域内的变量不被外部使用?如果不用 block-scoped-functions
而仅用 block-scoping
会怎么样呢?
1 | { |
_name
函数对外部作用域还是可知的,有一些区别。
babel-plugin-transform-function-name
再是一些不算新特性的插件吧,比如这个给匿名函数增加函数名
1 | const foo = function () {} |
编译成
1 | const foo = function foo() {}; |
总结
可以发现,真正有用的插件大概有 10 个,但每次都会安装全部的,即使使用 @babel/preset-env
,它也仅仅是根据浏览器可以去掉一些插件,但如果我们能根据项目中实际使用的语法,只配置 plugin
,才是最精简的方式。
但首先要求对项目中所使用的语法,以及新特性对应的插件非常了解,才能做到有的放矢。