Babel的使用笔记

写在前面

先理解下babel存在的意义,babel的出现是为了解决不同的运行环境对于es语言的差异性,主要包括2类,语法的转译和特性的支持。
本文主要介绍babel 7,此后的模块包都是以@bable开头的。

主要思路

举个栗子:你是个开饭店的,有一个客人来你店里点了一份牛排外卖,然后你做出来送到他的住所。那么完成这件事情需要几个步骤:

  1. 客人下单
  2. 你将生牛肉摆上
  3. 准备点缀物品
  4. 准备调料
  5. 操刀并根据客人要求加入点缀物品和调料
  6. 然后一顿操作
  7. 最后将10分熟的牛肉送到客人面前

大致是这样,先记住这个栗子,我们继续往下看。

主要模块包

  1. @babel/cli
  2. @babel/preset-env
  3. @babel/polyfill
  4. @babel/core
  5. @babel/plugin-transform-xxx(其他插件)

    • @babel/runtime
    • @babel/plugin-transform-runtime
    • @babel/plugin-transform-arrow-functions
    • 其他

@babel/cli

@babel/cli是babel提供的内建的命令行工具,主要是提供babel这个命令来对js文件进行编译。
这里要注意它与另一个命令行工具@babel/node的区别,首先要知道他们二者都是命令行工具,但是官方文档明确对他们定义了他们各自的使用范围:
@babel/cli 是一个适合安装在本地项目里,提供babel命令,@babel/node是一个全局安装提供babel-node命令。
将此包安装至开发依赖中可以在在当前项目中使用babel命令,并可以添加参数然后将转译后的代码输出到目标文件中

执行

./node_modules/.bin/babel src/testItems/babel --out-dir src/testItems/dist

或者

npx babel src/testItems/babel --out-dir src/testItems/dist

会发现testItems下会生成一个文件夹dist及其下面的index.js文件,内容如下

console.log([1, 2, 3].findIndex(x => x == 4));

const alertMe = msg => {
  console.log(msg);
};

呵呵,和我的源码一毛一样,那有个毛用!!!
不要慌,这是因为我们还没有对源码做相应的处理,就想做牛肉一样,总不能拿盘生牛肉给客户吧,是时候准备材料开始拿刀了!

@babel/preset-env

先看一个简单例子, 这里的@babel/core后面会讲到

const babel = require("@babel/core");

babel.transform(
  ` 
    console.log([1,2,3].findIndex(x => x == 4))
    const alertMe = (msg) => {
        console.log(msg)
    }
    
   `,
  {
    plugins: ["@babel/plugin-transform-arrow-functions"]
  },
  function(err, result) {
    console.log(result);
  }
);

执行

node src/testItems/babel/index.js

我们运行看下输出

{ metadata: {},
  options:
   { babelrc: false,
     configFile: false,
     passPerPreset: false,
     envName: 'development',
     cwd: 'd:\\我的项目\\bq-test',
     root: 'd:\\我的项目\\bq-test',
     plugins: [ [Plugin] ],
     presets: [],
     parserOpts:
      { sourceType: 'module', sourceFileName: undefined, plugins: [] },
     generatorOpts:
      { filename: undefined,
        auxiliaryCommentBefore: undefined,
        auxiliaryCommentAfter: undefined,
        retainLines: undefined,
        comments: true,
        shouldPrintComment: undefined,
        compact: 'auto',
        minified: undefined,
        sourceMaps: false,
        sourceRoot: undefined,
        sourceFileName: 'unknown' } },
  ast: null,
  code:
   'console.log([1, 2, 3].findIndex(function (x) {\n  return x == 4;\n}));\n\nconst alertMe = function (msg) {\n  console.log(msg);\n};',
  map: null,
  sourceType: 'module' }

可以看出这里的箭头函数已经被转译了,这就是@babel/plugin-transform-arrow-functions的作用。

但是在我们实际的开发中会用到更多的新语法,我们不可能一个个的添加插件吧,到这里我们可以看下@babel/preset-env这个插件,简单理解字面意思就是"预设",那到底是啥意思呢,大致意思就是官方帮我们准备的好的插件集合(包含@bable/preset-es2015、@bable/preset-es2016、@bable/preset-es2017),修改代码如下会得到相同输出

const babel = require("@babel/core");

babel.transform(
  ` 
    console.log([1,2,3].findIndex(x => x == 4))
    const alertMe = (msg) => {
        console.log(msg)
    }
    
   `,
  {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: {
            node: "4"
          }
        }
      ]
    ]
    // plugins: ["@babel/preset-env"]
  },
  function(err, result) {
    console.log(result);
  }
);

@babel/polyfill

我们再仔细看看这里

code:
   'console.log([1, 2, 3].findIndex(function (x) {\n  return x == 4;\n}));\n\nconst alertMe = function (msg) {\n  console.log(msg);\n};',
  map: null,

会发现除了箭头函数被转译了之外,findIndex方法并没有任何的变化,那是为什么呢,这里理解下语法和特性的区别,语法指的是类似箭头函数等编码方式,特性指的是标准api。后者需要@babel/polyfill来解决。

注意,使用 --save 参数而不是 --save-dev,因为这是一个需要在你的源码之前运行的 polyfill。
  • 和@babel/preset-env 一起用的时候

    • 如果在.babelrc 中指定 useBuiltIns: 'usage'的话,那么就不要在webpack.config.js 的 entry array 和source 中包含 @babel/polyfill 了。注意,@babel/polyfill 依然需要安装
    • 如果在.babelrc 中指定 useBuiltIns: 'entry'的话,那么就要和上面讨论的一样,在你应用的入口文件顶部通过require 或者 import 引入@babel/polyfill.
    • 如果在.babelrc 中没有指定 useBuiltIns 的值或者 设置 useBuiltIns: false. 可以直接在webpack.config.js 的 entry array 中添加 @babel/polyfill
    module.exports = {
          entry: ['@babel/polyfill', './app']
      }
  • 如果没有使用@babel/preset-env.那么就可以像我们上面讨论的一样把@babel/polyfill 添加到webpack 的entry array 中。你也可以直接通过import 或require 把它添加到应用的入口文件顶部。但是我们并不推荐这么做

@babel/core

@babel/core 是babel的核心包,起到转译的作用,也就是我们前面提到的刀,主要方法有transform等
简单试用下:

下面添加配置文件来使用babel命令进行编译
主要有一下几种方式创建配置文件

  1. babel.config.js
在项目的根目录(package.json 文件所在目录)下创建一个名为 babel.config.js 的文件,并输入如下内容。
module.exports = function (api) {
  api.cache(true);

  const presets = [ ... ];
  const plugins = [ ... ];

  return {
    presets,
    plugins
  };
}
  1. .babelrc
在你的项目中创建名为 .babelrc 的文件,并输入以下内容。
{
  "presets": [...],
  "plugins": [...]
}
  1. package.json
或者,还可以选择将 .babelrc 中的配置信息作为 babel 键(key)的值添加到 package.json 文件中,如下所示:
{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}
  1. .babelrc.js
与 .babelrc 的配置相同,但你可以使用 JavaScript 编写。
const presets = [ ... ];
const plugins = [ ... ];

module.exports = { presets, plugins };

这里使用第一种配置方法

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

  const presets = [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "2",
        },
        "corejs": "3", 
        "useBuiltIns": "usage"
      }
    ]
  ];

  return {
    presets
  };
};

说明:

  1. targets指定需要兼容的运行环境,也就是这里编译的代码能在node版本是2的node环境运行
  2. 使用"useBuiltIns": "usage" 表示会使用@bable/polyfill

其他插件

@babel/plugin-transform-runtime

"presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "4"
        }
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

编译后的代码

"use strict";

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

require("core-js/modules/es.array.find-index");

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

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

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

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

console.log([1, 2, 3].findIndex(function (x) {
  return x == 4;
}));

var alertMe = function alertMe(msg) {
  console.log(msg);
};

会发现createClass等比较通用的方法被提出来了,便于减少重复代码
当然还有其他的插件,这里就不一一列举了

总结

最后回顾一下开头的栗子做一下总结:

  1. @babel/cli 就是客户发出的订单要求也就是代码当前工程的运行指令
  2. 源代码就是生牛肉作为输入
  3. @bable/preset-env(还有其他的一些预设包集合)就是点缀物品改变原代码的表达形式
  4. @bable/polyfill就是调料提供源代码的性特性补充
  5. @bable/core就是操刀了或者其他的一些操作工具
  6. 然后结合一些其他插件如@babel/plugin-transform-runtime等插件一顿操作
  7. 最后生成10分兼容的编译代码

栗子不是很恰当,旨在帮助自己理解和记忆。

你可能感兴趣的:(html5,javascript)