资讯专栏INFORMATION COLUMN

Babel 7使用总结

seasonley / 3397人阅读

摘要:使用总结本文基于。可以引入纯净版的配置项中的属性可以方便的使用。例如使用时,需要先安装配置文件为得到的结果是可以看到引入时得到了一个别名,可以避免全局变量污染,但是可以发现实例方法并没有得到相应的处理。

Babel 7使用总结

​ 2019-07-08

本文基于Babel 7.4.5。


​ Babel主要模块如上图所示,接下来将分别介绍。

1. @babel/core

@babel/core主要是进行代码转换的一些方法,可以将源代码根据配置转换成兼容目标环境的代码。

import * as babel from "@babel/core";
babel.transform("code();", options, function(err, result) {
  result.code;
  result.map;
  result.ast;
});
2. @babel/cli

@babel/cli是 babel 提供的命令行工具,用于命令行下编译源代码。

首先安装依赖:

npm install --save-dev @babel/core @babel/cli

新建一个js文件:

let array = [1,2,3,4,5,6];
array.includes(function(item){
    return item>2;
})
class Robot {
    constructor (msg) {
        this.message = msg
    }
    say () {
        alertMe(this.message)
    }
}
Object.assign({},{
    a:1,b:2
})
const fn = () => 1;
new Promise();

执行转换:

npx babel index.js --out-file out.js

可以发现输出代码没有变化,这是因为没有进行配置来确定怎么进行转换。

3. @babel/plugin*

babel是通过插件来进行代码转换的,例如箭头函数使用plugin-transform-arrow-functions插件来进行转换。

首先安装该插件:

npm install --save-dev @babel/plugin-transform-arrow-functions

可以通过@babel/cli传参或者配置文件的方式使用插件:

@babel/cli

npx babel index.js --out-file out.js --plugins=@babel/plugin-transform-arrow-functions

则可以得到out.js文件,可以看到箭头函数已经被转换。

let array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});
class Robot {
    constructor (msg) {
        this.message = msg
    }
    say () {
        alertMe(this.message)
    }
}
Object.assign({}, {
  a: 1,
  b: 2
});
const fn = function () {
  return 1;
};

new Promise();

配置文件babel.config.js(javascript写法)或.babelrc(json写法),使用配置文件是更加常用的方式。

module.exports = function (api) {
    api.cache(true);

    const plugins = [ "@babel/plugin-transform-arrow-functions" ];

    return {
        plugins
    };
}

4. @babel/presets

我们在index.js中使用了多种es6的语法,一个个的导入插件很麻烦,presets是一组预设好的插件集合。官方为常见环境组装了一些 presets (当然也可以自己配置):

@babel/preset-env

@babel/preset-flow

@babel/preset-react

@babel/preset-typescript

我们使用@babel/preset-env为例(使用前需npm install @babel/preset-env):

module.exports = function (api) {
    api.cache(true);
    const presets =  [
        ["@babel/preset-env"]
    ];
    return {
        presets
    };
}

得到的结果如下, 可以看到箭头函数被编译、es6类、let声明被编译了。

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});

var Robot =
/*#__PURE__*/
function () {
  function Robot(msg) {
    _classCallCheck(this, Robot);

    this.message = msg;
  }

  _createClass(Robot, [{
    key: "say",
    value: function say() {
      alertMe(this.message);
    }
  }]);

  return Robot;
}();

Object.assign({}, {
  a: 1,
  b: 2
});

var fn = function fn() {
  return 1;
};

new Promise();

但是可以看到数组的实例方法includes、对象的静态方法,以及promise并没有被编译。

这是因为babel 把 Javascript 语法为syntax 和 api, api 指那些我们可以通过 函数重新覆盖的语法 ,类似 includes, map, includes, Promise, 凡是我们能想到重写的都可以归属到api。syntax 指像箭头函数,let,const,class, 依赖注入 Decorators等等这些,我们在 Javascript在运行是无法重写的,想象下,在不支持的浏览器里不管怎么样,你都用不了 let 这个关键字。

@babel/presets默认只对syntax进行转换,我们需要使用@babel/polyfill来提供对api的的支持。

5. @babel/polyfill

@babel/polyfill由core-js2和regenerator-runtime组成,后者是facebook开源库,用来实现对generator、async函数等的支持,前者是js标准库,包含不同版本javascipt语法的实现。

只要在js文件的入口顶部引入@babel/polyfill就可以在后问的代码中自由的使用es6 api了。

但是整体@babel/polyfill整个包体积较大,我们通常只使用了其中一部分方法,而引入整个库显然是不合适的。所以你可以只引入使用的方法:

import "core-js/features/array/from"; // <- at the top of your entry point
import "core-js/features/array/flat"; // <- at the top of your entry point
import "core-js/features/set";        // <- at the top of your entry point
import "core-js/features/promise";    // <- at the top of your entry point

Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32

如果你不想污染全局命名空间(例如在写一个npm库时,要保持其隔离性)。可以引入纯净版:

import from from "core-js-pure/features/array/from";
import flat from "core-js-pure/features/array/flat";
import Set from "core-js-pure/features/set";
import Promise from "core-js-pure/features/promise";

from(new Set([1, 2, 3, 2, 1]));                // => [1, 2, 3]
flat([1, [2, 3], [4, [5]]], 2);                // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32

preset-env的配置项中的useBuiltIns属性可以方便@babel/polyfill的使用。

useBuiltIns:false(default):此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill

useBuiltIns:"entry":根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 import "@babel/polyfill",会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill

useBuiltIns:"usage":不需要在文件顶部手动引入@babel/polyfill,会根据代码中的使用进行按需添加。

在这里使用useBuiltIns:"usage"作为示例,babel.config.js文件如下:

module.exports = function (api) {
    api.cache(true);

    const presets =  [
        ["@babel/preset-env",
            {
            "useBuiltIns": "usage",
            "targets":{
                "browsers":["> 1%", "last 2 versions", "not ie                                                             <= 8"]
                }
            }
        ]
    ];
    return {
        presets,
        // plugins
    };
}

得到的编译结果:

"use strict";

require("core-js/modules/es6.promise");

require("core-js/modules/es6.object.to-string");

require("core-js/modules/es6.object.assign");

require("core-js/modules/es7.array.includes");

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});

var Robot =
/*#__PURE__*/
function () {
  function Robot(msg) {
    _classCallCheck(this, Robot);

    this.message = msg;
  }

  _createClass(Robot, [{
    key: "say",
    value: function say() {
      alertMe(this.message);
    }
  }]);

  return Robot;
}();

Object.assign({}, {
  a: 1,
  b: 2
});

var fn = function fn() {
  return 1;
};

new Promise();

可以看到实现了polyfill的按需引入。但是在配置文件中未指定core-js版本时,默认会使用core-js2。命令行会出现如下提示:

WARNING: We noticed you"re using the useBuiltIns option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the corejs option.

这是因为core-js3已经发布,@babel/polyfill不支持从core-js2到core-js3的平滑过渡,所以在babel 7.4版本中,已经废弃@babel/polyfill(只能用core-js2),而是直接引入core-js3和regenerator-runtime代替。

import "@babel/polyfill";

// migration

import "core-js/stable";
import "regenerator-runtime/runtime";

使用core-js3有很多优点,首先就是新,包含很多新特性,其次就是可以配合@babel/runtime(后文详述)。更多优点见core-js@3, babel and a look into the future。

使用core-js3是 babel.config.js如下:

module.exports = function (api) {
    api.cache(true);

    const presets =  [
        ["@babel/preset-env",
            {
            "useBuiltIns": "usage",
            "corejs":3,
            "targets":{
                "browsers":["> 1%", "last 2 versions", "not ie <= 8"]
                }
            }
        ]
    ];
    return {
        presets,
        // plugins
    };
}

仔细观察上面的编译结果可以发现有两个问题。

高阶语法向低阶语法转化时引入了了很多helper函数(如_classCallCheck)。当文件数量很多时,每个文件都引入这些helper函数会使得文件体积增大,怎么这些helper函数抽离到多带带的模块,然后按需引入呢?

虽然polyfill是按需引入的,但是会污染全局命名空间,当你写的是公共库时,可能会与使用者本地的方法产生冲突。例如你在你的库中引入了polyfill中的Promise,使用者自身定义了自己的Promise,这就容易产生冲突。如何将你的公共库中引入的polyfill api隔离起来呢?

要解决这两个问题,就要需要使用@babel/runtime和@babel/plugin-transform-runtime了。

6. @babel/runtime

@babel/runtime依赖@babel/helpers和regenerator-runtime,helper函数都可以从这里面引入,手动的肯定不可能,于是 babel 提供了 @babel/plugin-transform-runtime 来替我们做这些转换。

babel.config.js文件为:

module.exports = function (api) {
    api.cache(true);

    const presets =  [
        ["@babel/preset-env",
            {
            "useBuiltIns": "usage",
            "targets":{
                "browsers":["> 1%", "last 2 versions", "not ie <= 8"]
                }
            }
        ]
    ];
    const plugins = [
        ["@babel/plugin-transform-runtime"]
    ]

    return {
        presets,
        plugins
    };
}

得到的编译结果是:

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es6.object.assign");

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
require("core-js/modules/es7.array.includes");

var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});
var Robot =
/*#__PURE__*/
function () {
  function Robot(msg) {
    (0, _classCallCheck2.default)(this, Robot);
    this.message = msg;
  }

  (0, _createClass2.default)(Robot, [{
    key: "say",
    value: function say() {
      alertMe(this.message);
    }
  }]);
  return Robot;
}();
Object.assign({}, {
  a: 1,
  b: 2
});
var fn = function fn() {
  return 1;
};
new Promise();

可以看到我们的第一个问题已经圆满解决了。

解决第二个问题需要使用@babel/plugin-transform-runtime option中的corejs参数。默认为false,不对polyfill进行处理。可以设为不同版本的core-js。

例如使用core-js2时,需要先安装

npm install --save @babel/runtime-corejs2

配置文件为:

module.exports = function (api) {
    api.cache(true);

    const presets =  [
        ["@babel/preset-env",
            {
            "useBuiltIns": "usage",
            "targets":{
                "browsers":["> 1%", "last 2 versions", "not ie <= 8"]
                }
            }
        ]
    ];
    const plugins = [
        ["@babel/plugin-transform-runtime",{corejs:2}]
    ]

    return {
        presets,
        plugins
    };
}

得到的结果是:

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");

var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));

var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign"));

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

var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));

require("core-js/modules/es7.array.includes");

var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});

var Robot =
/*#__PURE__*/
function () {
  function Robot(msg) {
    (0, _classCallCheck2.default)(this, Robot);
    this.message = msg;
  }

  (0, _createClass2.default)(Robot, [{
    key: "say",
    value: function say() {
      alertMe(this.message);
    }
  }]);
  return Robot;
}();

(0, _assign.default)({}, {
  a: 1,
  b: 2
});

var fn = function fn() {
  return 1;
};

new _promise.default();

可以看到polyfill引入时得到了一个别名,可以避免全局变量污染,但是可以发现实例方法includes并没有得到相应的处理。这是core-js2没有解决的问题,随着2019年3月core-js3的发布,这个问题得到了完美解决。我们将corejs设为3,得到了结果如下:

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

var _assign = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/assign"));

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

var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));

var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));

var array = [1, 2, 3, 4, 5, 6];
(0, _includes.default)(array).call(array, function (item) {
  return item > 2;
});

var Robot =
/*#__PURE__*/
function () {
  function Robot(msg) {
    (0, _classCallCheck2.default)(this, Robot);
    this.message = msg;
  }

  (0, _createClass2.default)(Robot, [{
    key: "say",
    value: function say() {
      alertMe(this.message);
    }
  }]);
  return Robot;
}();

(0, _assign.default)({}, {
  a: 1,
  b: 2
});

var fn = function fn() {
  return 1;
};

new _promise.default();
7. @babel/register

经过 babel 的编译后,我们的源代码与运行在生产下的代码是不一样的。

babel-register 则提供了动态编译。换句话说,我们的源代码能够真正运行在生产环境下,不需要 babel 编译这一环节。

我们先在项目下安装 babel-register:

$ npm install --save-dev @babel/register

然后在入口文件中 require

require("@babel/register")
require("./app")

在入口文件头部引入 @babel/register 后,我们的 app 文件中即可使用任意 es2015 的特性。

当然,坏处是动态编译,导致程序在速度、性能上有所损耗。(我们在启动测试脚本的时候可以使用)

7. @babel/node

我们上面说,babel-register 提供动态编译,能够让我们的源代码真正运行在生产环境下 - 但其实不然,我们仍需要做部分调整,比如新增一个入口文件,并在该文件中 require("@babel/register")。而 babel-node 能真正做到一行源代码都不需要调整:

$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.js

只是,请不要在生产环境中使用 babel-node,因为它是动态编译源代码,应用启动速度非常慢

参考

http://babel.docschina.org/docs/en/babel-plugin-transform-runtime#technical-details

https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md

https://blog.hhking.cn/2019/0...

https://segmentfault.com/a/11...

https://zhuanlan.zhihu.com/p/...

https://blog.zfanw.com/babel-...

https://www.thebasement.be/up...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105507.html

相关文章

  • babel相关总结

    对babel一直没具体总结过,趁周末看了下文档,记录一下 babel作为一个compiler,主要用在转换新的es标准实现来使所有浏览器都支持,这包含两方面 新的es标准语法,箭头函数、扩展运算符、块级作用域等 转化新的es标准方法或正被提议还未纳入标准的方法,,Array.from、Map、Promise、String.includes等 babel编译过程 babel的编译过程分为三个阶段...

    Richard_Gao 评论0 收藏0
  • Babel 7 转码的正确姿势

    摘要:转码的配置是每位前端童鞋在日常工作中都会遇到的。简单点来说就是在转码过程中,对于一些新语法,都会抽象一个个小的函数,在转码过程中完成替换。以上即是我总结的转码姿势,如果对本篇有疑问或建议,欢迎在这里提出。 Babel 转码的配置是每位前端童鞋在日常工作中都会遇到的。刚开始我也是在网上搜索各种配置方法,升级到 Babel 7 的时候又折腾了一把,所以决定把自己的心得和理解记录下来,希望能...

    JohnLui 评论0 收藏0
  • 从搭建脚手架到在npm上发布react组件

    摘要:从搭建脚手架到在上发布组件最近公司给公司里架设了私有的仓库,相应地也需要一个用来发布组件用的脚手架,在这个过程中又又又又复习了一下,在这里分享下脚手架搭建的过程。 从搭建脚手架到在npm上发布react组件 最近公司给公司里架设了私有的npm仓库,相应地也需要一个用来发布react组件用的脚手架,在这个过程中又又又又复习了一下webpack,在这里分享下脚手架搭建的过程。 首先,我们预...

    junfeng777 评论0 收藏0
  • vuex总结

    摘要:最近用了,遇到一个小问题在使用对象展开运算符的时候报错,说找不到,但是明明已经引用了,如下在网上查阅了问题,发现是的问题,的配置文件没有写当然需要先安装这些包这样就可以愉快地使用对象扩展运算符来用了 最近用了vuex,遇到一个小问题:在使用对象展开运算符...mapState,...mapAction的时候报错,说找不到,但是明明已经引用了,如下: showImg(https://se...

    fasss 评论0 收藏0
  • babel使用方法总结

    摘要:在项目根目录下创建一个文件,注意这是个配置文件,以点号开头,没有后缀。提供了一种可以在浏览器中使用的方法,只需两步就能一劳永逸。 1.在命令行中使用 Babel Babel 官方推荐将 Babel 安装在本地,因为 Babel 的不同版本以及不同转码规则会起到不同的效果,全局安装会带来不必要的麻烦。在命令提示符中转到自己的项目目录下: npm install --save-dev ba...

    沈俭 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<