ES6和Babel你不知道的事儿

因babel的版本从5升级到6有不少改动,好比babel自己再也不提供任何transform的工做,都须要借助插件来完成,本文的全部讨论都是创建在babel 6之上的。若是只想看结论,直接跳到文章最后。 ---写在前面javascript

ES6即ECMAScript 6,是前端开发的JS最新规范,如今你们的开发都在使用ES6,对此并不陌生了。只是浏览器对ES6的支持并不完整,想要更好让ES6在各个平台完美运行还需一番折腾,特此一叙。下图是ES6的浏览器兼容性一览表(已ES6的Number为例):前端

图片描述

另外国内浏览器都号称是“双核”,实际上浏览器的版本号较新,打包的Webkit内核确很低,好比市场份额很高的360安全浏览器,最新版本是9.1,Webkit内核版本才是55.0.2883,要知道Chrome官方最新版本已经是62.0.3202,相差之远不甚理解。java

  • 版本截图:

    图片描述

  • userAgent:
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 QIHU 360SE"

固然,搜狗浏览器市场份额也不低,官方最新版本是7.1,内核版本是49.0.2623,为之一惊。node

  • 版本截图:
    图片描述
  • userAgent:
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0"

所以,咱们想要在全部的浏览器上平稳、完美的运行ES6代码,必需要了解它的忠实伴侣 Babelwebpack

你们可能对此也并不陌生,可是对babel的内部机制、插件的做用、兼容的配置还缺少一些认识,本文就是特地尝试来补全这些内容。git

使用babel无非要用到.babelrc文件或者在package.json增长babel字段。咱们以.babelrc文件为例:github

{
  "presets":["es2015","stage-0"],
  "plugins": ["transform-runtime"]
}

这是最多见的babel配置,而后结合webpack下的babel-loader完成对JS代码的babel编译。web

上面代码的presets和plugins分别是什么含义呢?若是是下面的配置有何不可呢?shell

{
  "presets":["es2015","stage-0"]
}

首先来明确一个概念: presets是一系列plugin的集合。好比上述配置中es2015表示babel-preset-es2015,它包含如下plugin:npm

  • check-es2015-constants
  • transform-es2015-arrow-functions
  • transform-es2015-block-scoped-functions
  • transform-es2015-block-scoping
  • transform-es2015-classes
  • transform-es2015-computed-properties
  • transform-es2015-destructuring
  • transform-es2015-duplicate-keys
  • transform-es2015-for-of
  • transform-es2015-function-name
  • transform-es2015-literals
  • transform-es2015-modules-commonjs
  • transform-es2015-object-super
  • transform-es2015-parameters
  • transform-es2015-shorthand-properties
  • transform-es2015-spread
  • transform-es2015-sticky-regex
  • transform-es2015-template-literals
  • transform-es2015-typeof-symbol
  • transform-es2015-unicode-regex
  • transform-regenerator

使用presets的好处就是不用再plugins配置里一个一个的写了。

而后,咱们经过对代码的编译来看下上面两个配置的区别。源码以下:

let a=1;
let b=(item)=>{return item+1};
let c='1'.padStart(2,'0');
let d=Object.assign({k:2},{t:4});
let e=new Set();

咱们使用第二种配置,获得的编译结果以下:

"use strict";
var a = 1;
var b = function b(item) {
  return item + 1;
};
var c = '1'.padStart(2, '0');
var d = Object.assign({ k: 2 }, { t: 4 });
var e = new Set();

从编译结果来看,let、箭头函数都被编译了,然而padStart和Object.assign原样输出了。缘由很简单,let被编译是使用了es2015中的transform-es2015-block-scoping,箭头函数编译是使用了es2015的transform-es2015-arrow-functions。padStart、Object.assign和Set并未在es2015和state-0中找到对应plugin。咱们再使用第一种配置编译,结果以下:

'use strict';
var _set = require('babel-runtime/core-js/set');
var _set2 = _interopRequireDefault(_set);
var _assign = require('babel-runtime/core-js/object/assign');
var _assign2 = _interopRequireDefault(_assign);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var a = 1;
var b = function b(item) {
  return item + 1;
};
var c = '1'.padStart(2, '0');
var d = (0, _assign2.default)({ k: 2 }, { t: 4 });
var e = new _set2.default();

从编译结果看,let、箭头函数、Object.assign、Set都被正确编译,padStart仍岿然不动。在这友情提醒一下,babel-plugin-transform-runtime是依赖babel-runtime的。那么如何让padStart方法也能被成功编译呢,这么大的开场白终于聊到了咱们今天的主题:babel的polyfill方案。

官方推荐的方式是使用babel-polyfill。

This will emulate a full ES2015+ environment and is intended to be used in an application rather than a library/tool. This polyfill is automatically loaded when using babel-node.

This means you can use new built-ins like Promise or WeakMap, static methods like Array.from or Object.assign, instance methods like Array.prototype.includes, and generator functions (provided you use the regenerator plugin). The polyfill adds to the global scope as well as native prototypes like String in order to do this.

用最简单的方式归纳官方的说法就是:只要引入了babel-polyfill你能够大胆的用ES6。基本方法以下:

  1. 先安装

    npm install --save babel-polyfill
  2. 后使用

    // 在代码中显示调用
    require("babel-polyfill");
    // or
    import "babel-polyfill";

    或者在webpack中配置

    module.exports = {
      entry: ["babel-polyfill", "./index.js"]
    };

那么问题来了,既然使用这么简单,有啥弊端没?这个问题的答案也很简单:它无疑大大增长了代码的体积,即便你只有1k的代码,也会打包出几百k出来。若是不在乎这个代码的体积,肆意大胆的去用吧。若是想作到代码清爽、合理的利用babel的功能还请继续阅读。

使用babel-polyfill能够不使用presets和transform-runtime,可是不意味着presets和transform-runtime没有用武之地。在此总结了几个原则:

  1. 纯业务开发

    • 第1、先要考虑全部兼容的平台和环境,选择性的使用babel-polyfill和transform-runtime
      一般状况下业务代码较重,再加上业务逻辑复杂的话,使用的ES6语法比较全面很频繁,还要考虑到各个小伙伴的代码兼容性,使用babel-polyfill结合webpack抽离出公共代码库,总体上仍是能节省代码体积的。若是想全盘使用babel-polyfill而且在此基础上进行优化的话,请参考第二点。
    • 第2、放弃使用preset-es201五、preset-state-0,请使用preset-env
      babel-polyfill的引入会自动加入不少代码,有时候咱们并不彻底须要。好比:作移动端开发不须要考虑IE之类的、B端产品线只考虑指定的浏览器等。这个时候使用prest-env就能够了。配置以下:

      {
        "presets": [
          ["env", {
            "targets": {
              "browsers": ["last 2 versions", "safari >= 7"]
            }
          }]
        ]
      }

      browsers的可选值参考 browserslist;有几个点要提醒一下:

      • 上述的"last 2 versions"是主版本,这样配置会引入不少代码,建议针对性的配置。
      • 虽然使用了preset-env会针对指定的平台进行编译代码,可是要注意,即便你的代码都是ES5,打包出来的体积也不会小,由于它不是根据你的代码来选择性的编译,而是根据平台。若是想进一步优化,如根据平台也要根据代码来选择性的polyfill,请参考 @babel/preset-env
  2. 类库和工具开发

    • 第1、尽可能避免使用babel-polyfill而使用tranform-runtime

      If you are looking for something that won't modify globals to be used in a tool/library, checkout the transform-runtime plugin. This means you won't be able to use the instance methods mentioned above like Array.prototype.includes.

      官方这句话换个角度讲就是类库的开发建议使用transform-runtime,它的原则是不改变原型链上的方法,可是经过babel-runtime或者core-js手动引入,这样不只代码优雅编译的包体积会小不少。

    • 第2、选择性的使用ES6语法

      ES6虽然很强大,可是不少方法使用ES5仍可轻松实现。看下刚才说起的 Array.prototype.includes方法:

      // ES6写法
      let a=[1,2,3];
      if(a.includes(1)){
        console.log('1 is finded');
      }
      // ES5写法
      let a=[1,2,3];
      if(a.some((item)=>{return item===1})){
        console.log('1 is finded');
      }

      这两个写法区别并不大,可是若是使用ES6的写法再加上polyfill的引入,代码要多很多。

  3. 兼容到IE8-

    请老老实实使用babel-polyfill。

相关文章
相关标签/搜索