/ JavaScript

Babel 配置技巧

当下,每个写 JavaScript 的猿可能都在或多或少地使用 Babel。借助 Babel,我们可以使用最新的 ECMAScript 特性,而不用太关注浏览器支持。Babel 在背后进行了许多复杂的转换,我们要做的,就是写配置文件,告诉 Babel 我们想要什么。下面就来看看一些配置技巧吧。

.babelrc 中使用 JS

All Babel API options except the callbacks are allowed (because .babelrc files are serialized as JSON5).

Babel 使用 JSON5 解析 .babelrc,也就是说可以在 .babelrc 中使用宽松的语法,可以添加注释什么的,不过,真的可以像 ESLint 的 .eslintrc.js 直接使用 JS 配置吗?截止目前的最新版本(Babel 6),并不会解析 .babelrc.js 之类的文件,不过可以变通一下。

首先,建一个 .babelrc.js 文件:

const ES = process.env.BABEL_ENV === 'es'

module.exports = {
  presets: [
    [
      'env', { modules: !ES }
    ],
    'react',
    'stage-2',
  ],
};

接下来,再建一个 Babel 认识的 .babelrc 文件:

{
  "presets": ["./.babelrc.js"]
}

或者,嫌多一个文件麻烦的话,加在 package.json 里面也没问题:

{
  "babel": {
    "presets": [
      "./.babelrc.js"
    ]
  }
}

通过 JS 编写配置文件,不仅可以给一些参数复杂的插件传递正则、函数等参数,还可以根据环境变量设置不同的参数,产出不同的代码。

减少冗余代码

babel-plugin-transform-runtime

以下面的源代码为例:

class Person {
  constructor(options) {
    this.options = Object.assign({}, Person.DEAULTS, options);
  }

  sayHi() {
    return `Hi, this is ${this.options.name}`;
  }
}

Person.DEAULTS = {
  name: 'anonymous',
};

.babelrc 配置为:

{
  "presets": [
    "es2015"
  ],
  "plugins": ["transform-object-assign"]
}

转换后的代码为:

'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Person = function () {
  function Person(options) {
    _classCallCheck(this, Person);

    this.options = _extends({}, Person.DEAULTS, options);
  }

  _createClass(Person, [{
    key: 'sayHi',
    value: function sayHi() {
      return 'Hi, this is ' + this.options.name;
    }
  }]);

  return Person;
}();

Person.DEAULTS = {
  name: 'anonymous'
};

就是说,如果源代码中有 10 个类似的模块,就会有 10 份重复的 _extends_createClass_classCallCheck 代码分布在各个文件中,太多冗余。通过 babel-plugin-transform-runtime 可以有效解决这一问题。

安装并配置 Runtime transform 插件:

{
  "presets": [
    "es2015"
  ],
  "plugins": [
    "transform-object-assign",
    "transform-runtime"
  ]
}

转换出来的代码会引用公共的模块,干净多了:

'use strict';

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Person = function () {
  function Person(options) {
    (0, _classCallCheck3.default)(this, Person);

    this.options = (0, _extends3.default)({}, Person.DEAULTS, options);
  }

  (0, _createClass3.default)(Person, [{
    key: 'sayHi',
    value: function sayHi() {
      return 'Hi, this is ' + this.options.name;
    }
  }]);
  return Person;
}();

Person.DEAULTS = {
  name: 'anonymous'
};

记得把 babel-runtime 也加到依赖中。如果需进一步定制,可参考官方文档

生产环境中移除 React PropTypes 定义

React PropTypes 定义可以在开发时帮助我们校验传入的属性,但在构建生产环境代码时,完全可以移除,以减小文件体积,节省宽带。babel-plugin-transform-react-remove-prop-types 就是干这事的,具体参见文档,不再赘述。