babel入门教程

Babel

Babel 是一个 JavaScript 编译器,它能够将ES6+语法编译为浏览器支持的ES5语法。要学好babel必须先理解相关的概念,可是你刚起步就去扣这些细节的话,极可能由于babel一些复杂而模糊的概念打击你的信息。因此咱们先从最简单的开始,而后深刻。git

最简单的例子

接下来咱们尝试按照官方文档作一个最简单的例子github

安装babel-clinpm

npm install --save-dev babel-cli

添加babel编译命令api

"build": "babel src -d lib"

添加.babelrc配置文件浏览器

{
  "presets": [],
  "plugins": []
}

添加JS文件babel

// /src/main.js
const fetch = args => args;
console.log(Promise);

执行 npm run build,咱们发现编译后的文件没有发生任何的改变,就是简单的原样输出app

// /lib/main.js
const fetch = args => args;
console.log(Promise);

为何呢?这是由于在.babelrc文件没有作任何的配置。咱们一共使用了三个新特性,分别是常量申明const,箭头函数=>,新全局变量Promise。接下来我门尝试将他们编译为ES5代码。async

Alt 来自babel官方文档

如上图所示,借助plugin能够实现咱们的目标。接下来咱们分别实现它:函数

  • const

原文连接 文档中说明:工具

babel-plugin-check-es2015-constants 这个插件仅仅是验证const变量的规则,好比不能重复申明,不可变等特性。

把它编译为ES5代码须要借助babel-plugin-transform-es2015-block-scoping

咱们设置须要编译的代码为:

const fetch = args => args;
fetch = 55; // 故意写错
console.log(Promise);

安装上面所需的两个plugin并改写.babelrc配置:

{
  "presets": [],
  "plugins": [
    "check-es2015-constants",
    "transform-es2015-block-scoping"
  ]
}

编译过程报错验证check-es2015-constants生效
Alt text

咱们修改须要编译的代码为:

const fetch = args => args;
console.log(Promise);

再次编译成功

var fetch = args => args;
console.log(Promise);

到这里咱们已经成功的使用插件对const的语法和转译(Syntaxtransform)ES5化。是否是很开心呢? 接下来咱们处理箭头函数=>

  • arrow functions

一样安装对应的plugin :

npm install --save-dev babel-plugin-transform-es2015-arrow-functions

这里延用上面须要compile的源代码:

var fetch = args => args;
console.log(Promise);

改写.babelrc配置文件:

{
  "presets": [],
  "plugins": [
    "check-es2015-constants",
    "transform-es2015-block-scoping",
    "transform-es2015-arrow-functions"
  ]
}

编译结果:

var fetch = function (args) {
  return args;
};
console.log(Promise);

编译成功,unbelievable, we did it!!!,是否是真的很简单?先别得意😤😤😤,还有一个Promise没有解决呢。有惊喜哟!!!

  • Promise

我有一个问题:我特地将上面的两个例子放在一块儿,是由于他们都属于对新语法Syntax进行编译,而Promise是做为一个全局api的存在,在大部分浏览器中是不存在这个全局api的,若是让你来解决这个问题,你会怎么作?

你的答案:是的,没错!就是在不支持Promise的环境中实现Promise,在babel中被称为polyfill,注意它和shim的区别哦~

babel-polyfill 文档最简单的使用:

npm install --save-dev babel-polyfill

Use it by requiring it at the top of the entry point to your application or in your bundler config. ---在入口文件的最顶层做用域直接引入

到这里咱们已经完成既定的3个目标,可是你有没有想过,随着咱们ES6新特性的增长,plugin的长度也逐渐增长,能够碰见会有多长😅,并且本身去找这些对应的plugin也是比较麻烦的。有没有傻瓜式的集成方法呢?您接着往下看。

Preset

为了方便,Babel团队将一些Plugins集合在一块儿,并称之为preset。因此一个preset是一系列plugin的总和。按照年份划分:

ES2015/ ES-2016/ES2017 等等,还延伸了stage的概念,详情请查阅官网。

babel-preset-env

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.
不须要指定任何的pluginpolyfill,Babel preset 能够将ES6+的新语法向下编译为ES5代码,按照你指定的运行环境。具体的配置项请看官网。

Without any configuration options, babel-preset-env behaves exactly the same as babel-preset-latest (or babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 together).
不须要作任何配置选项,它和babel-preset-latest表现一致,或者说和babel-preset-es2015, babel-preset-es2016, babel-preset-es20174个preset总和一致。

感受是否是很牛逼!下面咱们来尝试一个例子。为防止有任何的代码污染,我卸载全部npm package

最初的样子:

{
  "name": "babelrc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "babel src -d lib"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {}
}

我安装时发现@babel/preset-env还处于bate版本,使用时还有一些问题,因此决定仍是使用老版本。

(1)安装babel-preset-env

Alt text

上图为babel-preset-env的一些依赖,主要为一些helperplugin

为了验证出babel-preset-env的是否知足要求,我新增了几行包括ES6的代码:

const fetch = args => args;
console.log(Promise);

// 新增一些代码
class G {

}

let [a, b, c] = [1, 2, 3];

(async () => {
  await console.log(1)
})();

(2)配置.babelrc

{
  "presets": ["env"],
  "plugins": []
}

(3)编译结果

"use strict";

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

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

var fetch = function fetch(args) {
  return args;
};
console.log(Promise);

// 新增一些代码

var G = function G() {
  _classCallCheck(this, G);
};

var a = 1,
    b = 2,
    c = 3;


_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
  return regeneratorRuntime.wrap(function _callee$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return console.log(1);

        case 2:
        case "end":
          return _context.stop();
      }
    }
  }, _callee, undefined);
}))();

我就问还有谁???全部的新语法特性都被处理了。

Runtime

官网文档

babel为源代码非实例方法(好比Object.assign)和 babel-runtime/helps下的工具函数自动引用了polyfill。这样能够避免全局空间的污染,很是适合用于JS库和工具包的实现。可是实例方法(好比someString.includes("target"))仍是须要使用babel-polyfill

若是你是新手,你可能没有注意到,babel编译时会在每一个文件生成一些须要帮助函数,若是文件比较多,那么这些重复的代码会增长编译后的代码体积。下面是一个例子:

源代码

class G {
  
}

.babelrc配置

{
  "presets": ["env"],
  "plugins": []
}

编译代码

"use strict";

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

var G = function G() {
  _classCallCheck(this, G);
};

// _classCallCheck就是一个内部生成的帮助函数

为了优化编译体积,babel 团队推出了 babel-plugin-transform-runtime + babel-runtime来解决这个问题。先看一下直观体验:

源代码

class G {
  
}

.babelrc配置

{
  "presets": ["env"],
  "plugins": ["transform-runtime"]
}

编译后的代码:

"use strict";

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

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

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

var G = function G() {
  (0, _classCallCheck3.default)(this, G);
};

经过对比能够看出,第二种方案直接从babel-runtime引入babel-runtime/helpers/classCallCheck,避免本身定义,从而减小代码的体积。

除了这个优势意外

  • babel-runtime

Alt text

三个主要部分:core.js + helpers+ regenerator

固然除了上面的方法,经过按需引入 polyfillstransforms更能带来更多的体积优化。

减小对没必要要浏览器的兼容来减小 polyfillstransforms的引入:

{
  "presets": [
    ["env", {
      "targets": {
        // The % refers to the global coverage of users from browserslist
        "browsers": [ ">0.25%", "not ie 11", "not op_mini all"]
      }
    }]
  ]
}

答疑

(1)可能你发现了我并无安装babel-cli,为何我能使用babel命令?

由于我在全局安装了babel

(2) browserslistrc 配置更改以后 babel编译后的代码怎么没有改变。或者说 browserslistrc 影响babel编译的具体表现是怎么样的?

暂时我也不知道

参考

[1] browserslist
[2] browserslist-example
[3] browserslist-queries

相关文章
相关标签/搜索