babel 简介

  1. babel 一词来自于希伯来语,直译为巴别塔,巴别塔象征 统一的国度、统一的语言 ,而今天的 JS 世界缺少一座巴别塔,不同版本的浏览器能识别的 ES 标准并不相同,就导致了开发者面对不同版本的浏览器要使用不同的语言,和古巴比伦一样,前端开发也面临着这样的困境;
  2. babel 的出现,就是用于解决这样的问题,它是一个编译器,可以把不同标准书写的语言,编译为统一的、能被各种浏览器识别的语言;
  3. 由于语言的转换工作灵活多样,babel 的做法和 postcsswebpack 差不多,它本身仅提供一些分析功能,真正的转换需要依托于插件完成;

babel 安装、使用

  1. babel 可以和构建工具联合使用,也可以独立使用,如果要独立的使用 babel 需要安装下面两个库:

    1. @babel/corebabel 核心库,提供了编译所需的所有 api
    2. @babel/cli:提供一个命令行工具,调用核心库的 api 完成编译;
  2. 安装 babel

    npm i -D @babel/core @babel/cli
    
  3. @babel/cli 的使用极其简单,它提供了一个命令 babel 可以按 文件 / 文件夹 编译;

    # 按文件编译
    babel 要编译的文件 -o 编辑结果文件
    
    # 按目录编译
    babel 要编译的整个目录 -d 编译结果放置的目录
    

babel 的配置

  1. 可以看到 babel 本身没有做任何事情,真正的编译要依托于 babel 插件babel 预设 来完成;

  2. 如何告诉 babel 要使用哪些插件或预设呢?需要通过一个配置文件 .babelrc

    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage", // 可以在编译结果中注入这些新的 API(promise 等),默认值为 false,表示不注入任何新的 API 
            "corejs": 3,
            "targets": {
              "chrome": "60"
            }
          }
        ]
      ],
      "plugins": ["@vue/babel-plugin-jsx", "@babel/plugin-transform-runtime"]
    }
    

babel 预设

  1. 预设是多个 bable 插件的集合,babel 有多种预设,最常见的预设是 @babel/preset-env@babel/preset-env 可以让你使用最新的 JS 语法,而无需针对每种语法转换设置具体的插件;

  2. @babel/preset-env 需要根据兼容的浏览器范围来确定如何编译,和 postcss 一样,可以使用文件 .browserslistrc 来描述浏览器的兼容范围;

    last 3 version
    > 1%
    not ie <= 8
    

babel 插件

  1. 插件在 Presets 前运行,插件顺序从前往后依次执行,Preset 顺序是颠倒的 (从后往前)

  2. 通常情况下 @babel/preset-env 只转换那些已经形成正式标准的语法,对于某些处于早期阶段、还没有确定的语法不做转换,如果要转换这些语法,就要单独使用插件;

  3. 注意:@babel/polyfill 已过时,目前被 core-js 和 generator-runtime 所取代;

@babel/plugin-proposal-class-properties

该插件可以让你在类中书写初始化字段

class A {
    a = 1;
    constructor(){
        this.b = 3;
    }
}

@babel/plugin-proposal-function-bind

该插件可以让你轻松的为某个方法绑定 this

遗憾的是,目前 vscode 无法识别该语法,会在代码中报错,虽然并不会有什么实际性的危害,但是影响观感

function Print() {
    console.log(this.loginId);
}

const obj = {
    loginId: "abc"
};

obj::Print(); // 相当于:Print.call(obj);

@babel/plugin-proposal-optional-chaining

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};

const baz = obj?.foo?.bar?.baz; // 42

const safe = obj?.qux?.baz; // undefined

babel-plugin-transform-remove-console

该插件会移除源码中的控制台输出语句

console.log("foo");
console.error("bar");

@babel/plugin-transform-runtime

用于项目提供一些公共的 API ,这些 API 会帮助代码转换

在 webpack 中使用 babel

JavaScript
json
browserslistrc
// webpack.config.js
module.exports = {
  mode: 'development',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
        ],
      },
    ],
  },
};
// .babelrc
{
    "presets": [
        ["@babel/preset-env", {
            "useBuiltIns": "usage",
            "corejs": 3
        }]
    ]
}
// .browserslistrc
last 3 version
> 1%
not ie <= 8

babel 转换案例分析

对类的转换

JavaScript
JavaScript
// source.js
class A {
    prop1 = 1;

    method2 = (...args) => {
        console.log(args);
    }

    constructor(b = 2) {
        this.prop2 = b;
    }

    method1() {
        console.log("method1");
    }

    static method3() {
        console.log("method3", this);
    }

    static method4 = () => {
        console.log("method4", this);
    }
}
// babel 的转换结果
"use strict";

function _instanceof(left, right) {
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
        // es6
        return !!right[Symbol.hasInstance](left);
    } else {
        // es5
        return left instanceof right;
    }
}

function _classCallCheck(instance, Constructor) {
    // 判断 instance 是不是 Constructor 的实例
    if (!_instanceof(instance, Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

//props: [{key:"xxx", value:"xxxxxx"}, {key:"ccc", value: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;
}

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true, // 可枚举
            configurable: true, // 可修改
            writable: true // 可被重写
        });
    } else {
        obj[key] = value;
    }
    return obj;
}

// 该立即执行函数的返回结果,应该是一个构造函数 A
var A = function () {
    // 构造函数 A,对应类中的 constructor
    function A() {
        // 转换:参数默认值
        var b = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;

        // 类调用检查
        _classCallCheck(this, A);

        // 定义一个属性:给 this 定义一个属性 prop1,赋值为 1,类似于 this.prop1 = 1;
        _defineProperty(this, "prop1", 1);

        // 将箭头函数方法,作为普通属性定义,箭头函数方法并不在原型上
        _defineProperty(this, "method2", function () {
            for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
                args[_key] = arguments[_key];
            }

            console.log(args);
        });

        this.prop2 = b;
    }

    // 为构造函数 A,定义原型方法,以及静态方法
    _createClass(A,
        [{
            key: "method1",
            value: function method1() {
                console.log("method1");
            }
        }],
        [{
            key: "method3",
            value: function method3() {
                console.log("method3", this);
            }
        }]);

    return A;
}();

_defineProperty(A, "method4", function () {
    console.log("method4", A);
});

async 和 await 的转换

JavaScript
JavaScript
// source.js
function A() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(3);
        }, 1000);
    })
}

async function B() {
    const b = await A();
    const c = await A();
    return b + c;
}

B().then(data => console.log(data));
// target.js(简化后的代码,相当于 生成器 + co 库)
"use strict";

function asyncGeneratorStep(gen, resolve, reject, _next, arg) {
    try {
        var info = gen.next(arg);
        var value = info.value;
    } catch (error) {
        reject(error);
        return;
    }

    if (info.done) {
        resolve(value);
    } else {
        Promise.resolve(value).then(data => {
            _next(data);
        });
    }
}


function A() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(3);
        }, 1000);
    });
}

function B() {
    var fn = function* () {
        const b = yield A();
        const c = yield A();
        return b + c;
    };
    return new Promise(function (resolve, reject) {
        var gen = fn();
        function _next(value) {
            asyncGeneratorStep(gen, resolve, reject, _next, value);
        }
        _next(undefined);
    });
}

B().then(function (data) {
    return console.log(data);
});
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. babel 简介
  2. 2. babel 安装、使用
  3. 3. babel 的配置
  4. 4. babel 预设
  5. 5. babel 插件
    1. 5.1. @babel/plugin-proposal-class-properties
    2. 5.2. @babel/plugin-proposal-function-bind
    3. 5.3. @babel/plugin-proposal-optional-chaining
    4. 5.4. babel-plugin-transform-remove-console
    5. 5.5. @babel/plugin-transform-runtime
  6. 6. 在 webpack 中使用 babel
  7. 7. babel 转换案例分析
    1. 7.1. 对类的转换
    2. 7.2. async 和 await 的转换