babel使用详解

1. bebel该如何使用

第一步 安装

npm install babel-cli --save-devcnpm install babel-cli --save-dev

如果想设置转码规则使用 cnpm install babel-preset-es2015 --save-dev

会在package.json中生成包如下(部分)

"babel-core": "^6.22.1",//babel转译器本身,提供了babel的转译API,如babel.transform等,用于对代码进行转译。像webpack的babel-loader就是调用这些API来完成转译过程的。
"babel-plugin-transform-runtime": "^6.22.0",//babel转译过程中使用到的插件,其中babel-plugin-transform-xxx是transform步骤使用的
"babel-preset-env": "^1.3.2",//transform阶段使用到的一系列的plugin
"babel-preset-stage-2": "^6.22.0",//transform阶段使用到的一系列的plugin
"babel-register": "^6.22.0",//通过绑定node.js的require来自动转译require引用的js代码文件
复制代码

(不建议安装在全局,因为不同项目可能会使用不同的bebel版本)

第二步 创建配置文件 .babelrc (放在根目录下,不建议在package.json里在babel字段添加设置)

{
  "presets": [//要加载和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按从尾到头的逆序运行(为了兼容用户使用习惯)
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
    //"es2015"
  ],
  "plugins": ["transform-runtime"],//该属性是告诉babel要使用那些插件,这些插件可以控制如何转换代码。要加载和使用的插件列表,插件名前的babel-plugin-可省略;plugin列表按从头到尾的顺序运行
  "env": {//指定在不同环境下使用的配置。比如production和development两个环境使用不同的配置,就可以通过这个字段来配置。env字段的从process.env.BABEL_ENV获取,如果BABEL_ENV不存在,则从process.env.NODE_ENV获取,如果NODE_ENV还是不存在,则取默认值"development"
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}
//同时设置了presets和plugins,那么plugins的先运行;
复制代码

babel会从当前转译的文件所在目录下查找配置文件,如果没有找到,就顺着文档目录树一层层往上查找,一直到.babelrc文件存在或者带babel字段的package.json文件存在为止。

第三步 webpack.base.conf.js中配置babel

module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      }
    ]
  }
复制代码

第四步 在package.json中配置项目的浏览器兼容情况

(具体参数参照[github.com/browserslis…])

 "browserslist": [
    "> 1%",
    "last 2 versions",
    "Android >= 3.2", 
    "Firefox >= 20", 
    "iOS 7"
  ]
复制代码

作用:

  1. 【.babelrc】文件可以针对配置
{
 "presets": [
   ["env", {
     "targets": {
       "browsers": ["last 2 versions"],
       "node": "current"
     },
   }]
 ]
}
复制代码
  1. 根据浏览器可以获得特性,比如最新的chrome浏览器支持原生的promise,而IE不支持,babel根据browserslist配置项就会动态的转义。不用在一个个进行配置了。

第五步 进入根目录,npm run build运行,获得打包文件

2. babel的工作原理

babel是一个转译器,它把同种语言的高版本规则翻译成低版本规则。

babel的转译过程分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

ES6代码输入 -> babylon进行解析 -> 得到AST -> plugin用babel-traverse对AST树进行遍历转译 -> 得到新的AST树
-> 用babel-generator通过AST树生成ES5代码
复制代码

【!!!注意!!!】 Babel默认只转换新的javascript语法,而不转换新的API,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象。以及一些在全局对象上的方法(比如 Object.assign)都不会转码。 比如说,ES6在Array对象上新增了Array.form方法,Babel就不会转码这个方法,如果想让这个方法运行,必须使用 babel-polyfill来转换等。

babel-polyfillbabel-runtime就是为了解决新的API与这种全局对象或全局对象方法不足的问题,因此可以使用这两个插件可以转换的。

3.babel-polyfill 使用方法

  1. 先安装包: npm install --save babel-polyfill
  2. 在入口处导入polyfill,保证polyfill代码在所有其他代码前先被调用 import "babel-polyfill"
  3. webpack配置:module.exports = { entry: ["babel-polyfill", "./app/js"] };

如果只是需要引入部分新原生对象或API,那么可以按需引入,而不必导入全部的环境,如下:

【通过core-js实现按需引入polyfill或runtime】

  1. core-js是polyfill、runtime的核心,因为polyfill和runtime其实都只是对core-js和regenerator的再封装,方便使用而已。
  2. polyfill和runtime都是整体引入的,不能做细粒度的调整,如果我们的代码只是用到了小部分ES6而导致需要使用polyfill和runtime的话,会造成代码体积不必要的增大(runtime的影响较小)。所以,按需引入的需求就自然而然产生了,这个时候就得依靠core-js来实现了。
  3. core-js有三种使用方式
  • 默认方式:require('core-js'):这种方式包括全部特性,标准的和非标准的,类似polyfill。

    require('core-js/fn/set');
    require('core-js/fn/array/from');
    require('core-js/fn/array/find-index');
    
    Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
    [1, 2, NaN, 3, 4].findIndex(isNaN);   // => 2
    
    复制代码
  • 库的形式: var core = require('core-js/library'):这种方式也包括全部特性,类似runtime,只是它不会污染全局名字空间,但是不能使用实例方法。

    var Set = require('core-js/library/fn/set');
    var from = require('core-js/library/fn/array/from');
    var findIndex = require('core-js/library/fn/array/find-index');
    
    from(new Set([1, 2, 3, 2, 1]));      // => [1, 2, 3]
    findIndex([1, 2, NaN, 3, 4], isNaN); // => 2
    复制代码

    想要使用prototype方法:通过::这个符号而不是.来调用实例方式,从而达到曲线救国的目的。这种方式的使用,路径中都会带有/virtual/

    import {fill, findIndex} from 'core-js/library/fn/array/virtual';
    
    Array(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4
    
    // 对比下polyfill的实现 
    // Array(10).fill(0).map((a, b) => b * b).findIndex(it => it && !(it % 8));
    复制代码
  • shim: require('core-js/shim')var shim = require('core-js/library/shim'): 这种方式只包括标准特性(就是只有polyfill功能,没有扩展的特性)

    举例说明

    • core-js/es6(core-js/library/es6)包含了全部的ES6特性,

    • core-js/es6/array(core-js/library/es6/array)只包含ES6的Array特性

    • core-js/fn/array/from(core-js/library/fn/array/from)只有Array.from这个实现

      (具体的每个特性和对应的路径可以参照[github.com/zloirock/co…])

4.babel-polyfill和babel-runtime的区别

  • babel-polyfill 会做兼容运行环境中并没有实现的一些方法。
  • babel-runtime 是将es6编译成es5去执行。使用es6的语法编写,最终会通过babel-runtime编译成es5。也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5。所以就有很多冗余的代码。
  • babel-polyfill 是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染。
  • babel-runtime不会污染全局对象和内置对象的原型,比如说我们需要Promise,我们只需要import Promise from 'babel-runtime/core-js/promise'即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。

【综上】虽然 babel-runtime 可以解决 babel-polyfill中的避免污染全局对象,但是它自己也有缺点的,比如我现在有100个文件甚至更多的话,就需要一个个文件加import Promise from 'babel-runtime/core-js/promise',为解决这一问题,引入了babel-plugin-transform-runtime。避免手动引入多个文件的import,并且它还做了公用方法的抽离。比如说有100个模块都使用promise,但是promise的polyfill仅仅存在1份。 这就是babel-plugin-transform-runtime插件的作用。

5.transform-runtime和babel-runtime的区别

babel-plugin-transform-runtime插件依赖babel-runtime,babel-runtime是真正提供runtime环境的包;也就是说transform-runtime插件是把js代码中使用到的新原生对象和静态方法转换成对runtime实现包的引用,举个例子如下:

// 输入的ES6代码
var sym = Symbol();
// 通过transform-runtime转换后的ES5+runtime代码 
var _symbol = require("babel-runtime/core-js/symbol");
var sym = (0, _symbol.default)();
复制代码

原本代码中使用的ES6新原生对象Symbol被transform-runtime插件转换成了babel-runtime的实现,既保持了Symbol的功能,同时又没有像polyfill那样污染全局环境(因为最终生成的代码中,并没有对Symbol的引用)

而且babel-runtime其实也不是真正的实现代码所在,真正的代码实现是在core-js中。

6.transform-runtime插件的功能

  1. 把代码中的使用到的ES6引入的新原生对象和静态方法用babel-runtime/core-js导出的对象和方法替代
  2. 当使用generators或async函数时,用babel-runtime/regenerator导出的函数取代(类似polyfill分成regenerator和core-js两个部分)
  3. 把Babel生成的辅助函数改为用babel-runtime/helpers导出的函数来替代(babel默认会在每个文件顶部放置所需要的辅助函数,如果文件多的话,这些辅助函数就在每个文件中都重复了,通过引用babel-runtime/helpers就可以统一起来,减少代码体积)
  4. babel-plugin-transform-runtime 的配置一些选项
  'plugins': [
    [
      'transform-runtime', 
      {
        'helpers': false,//默认值为true,表示是否开启内联的babel helpers(即babel或者环境本来存在的某些对象方法函数)如:extends,etc这样的在调用模块名字时将被替换名字。
        'polyfill': false,//默认值为true,表示是否把内置的东西(Promise, Set, Map)等转换成非全局污染的。
        'regenerator': true,//默认值为true,是否开启generator函数转换成使用regenerator runtime来避免污染全局域。
        'moduleName': 'flavortown/runtime'//默认值为 babel-runtime,引入规则:import extends from 'flavortown/runtime/helpers/extends';
      }
    ]
  ]
}
复制代码

【综上】:babel-runtime就是一个提供了regenerator、core-js和helpers的运行时库。transform-runtime在.babelrc里配置的时候,还可以设置helpers、polyfill、regenerator这三个开关,以自行决定runtime是否要引入对应的功能。

【注意1】:建议不要直接使用babel-runtime,因为transform-runtime依赖babel-runtime,大部分情况下都可以用transform-runtime达成目的。

【注意2】:由于runtime不会污染全局空间,所以实例方法是无法工作的(因为这必须在原型链上添加这个方法,这是和polyfill最大的不同) ,比如:

var arr = ['a', 'b', 'c'];
arr.fill(7);  // 实例方法不行
Array.prototype.fill.apply(arr, 7);  // 用原型链来调用也是不行
复制代码

转载于:https://juejin.im/post/5c0f39526fb9a04a0e2d09da

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值