Babel基礎知識

babel

先放一張Babel的基本流程圖javascript

一、babel是什麼

babel是一個JavaScript代碼轉換器,主要目的是將ES2015+的代碼轉換成llq或者其餘環境識別的代碼,主要目的有如下幾個:java

  • 轉換語法(babylon)
  • 兼容新方法( @babel/polyfill)
  • 源碼轉換(babel-generator)

二、babel的基本用法

2-一、經過引入babel核心包使用

安裝node

npm install @babel/core -D
複製代碼

使用git

const babel = require('@babel/core')
babel.transform("code",optionsObject)
複製代碼

Babel的核心包提供了一些最基礎的API,好比:babel.transformFilebabel.transformFromAst等等,具體可查看,這裏es6

2-二、經過命令行使用

@babel/cli包依賴@babel/core,因此須要同時安裝這兩個包。github

npm install @babel/core @babel/cli
複製代碼

安裝完這兩個包後,Babel提供了一個babel命令,假如如今要 將src文件夾下的js文件編譯到lib目錄下,執行以下命令:web

./node_modules/.bin/babel src --out-dir lib
複製代碼

或者:npm

npx babel src --out-dir lib
複製代碼

固然,在使用命令行時能夠給babel傳遞參數,好比下面這樣傳遞plugins:json

./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
複製代碼

或者傳遞presetsapi

./node_modules/.bin/babel src --out-dir lib --presets=@babel/env
複製代碼

三、插件和預設的區別

Babel核心包並不會去轉換代碼,核心包只提供一些核心API,真正的代碼轉換工做由插件或者預設來完成,好比要轉換箭頭函數,會用到這個plugin@babel/plugin-transform-arrow-functions,當須要轉換的要求增長時,咱們不可能去一一配置相應的plugin,這個時候就能夠用到預設了,也就是presetspresetsplugins的集合,一個presets內部包含了不少plugin

四、polyfill

Babel轉換代碼分爲兩部分,第一部分是將新的語法轉換爲普通語法,好比下面這樣,將ES6中的箭頭函數轉換爲ES5:

轉換前:

let fn = (a, b) => a + b;
複製代碼

轉換後:

var fn = function fn(a, b) {
  return a + b;
};
複製代碼

第二部分是,模擬新的API,好比說,咱們代碼中使用了Promise,而目標環境不支持Promise,那麼Babel會手動實現一個Promise,使得目標環境支持Promise,這個過程叫作polyfill

看個例子,下面咱們建立一個項目,目錄以下:

  • node_modules 項目依賴包
  • src 源文件
  • babel.config.js Babel的配置文件
  • package.json 項目描述文件

src文件下有個index.js文件,也就是須要編譯的文件,內容以下:

let count = 1; //let 聲明的變量
let fn = (a, b) => a + b;  //箭頭函數

let obj = { a: 1, b: 2 }
let b = { ...obj } //解構賦值


let promise = new Promise(() => {  //promise
    resolve(1)
})
function* it() {  //generator 函數
    yield 1;
    yield 2;
    return 3;
}
複製代碼

babel.config.js內容以下:

module.exports = {
    presets: [
    [
        "@babel/env",
            {
                useBuiltIns: "usage",//也能夠寫entry
                corejs: "2.6.9"
            },
        ]
    ]
}
複製代碼

編譯後代碼以下:

"use strict";

require("core-js/modules/es6.array.for-each");

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

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

require("core-js/modules/web.dom.iterable");

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

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

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

require("regenerator-runtime/runtime");

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

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

var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(it);

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }

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; }

var count = 1; //let 聲明的變量

var fn = function fn(a, b) {
  return a + b;
}; //箭頭函數


var obj = {
  a: 1,
  b: 2
};

var b = _objectSpread({}, obj); //解構賦值


var promise = new Promise(function (resolve) {
  //promise
  resolve(1);
});

function it() {
  return regeneratorRuntime.wrap(function it$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return 1;

        case 2:
          _context.next = 4;
          return 2;

        case 4:
          return _context.abrupt("return", 3);

        case 5:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}
複製代碼

編譯後的代碼量增長了不少,仔細分析大概分爲這幾部分:

一、require語句,大部分是從core.js包分別導入具體API對應的polyfill

二、由於使用了generator函數,須要去regenerator-runtime包中導入對應方法

三、對箭頭函數,解構賦值、generator函數進行改寫。

由於咱們在配置文件中配置了這項:

{
    useBuiltIns: "usage",
    corejs: "2.6.9"      
},
複製代碼

這個配置表示文件內用到了什麼API,那麼就轉換什麼API,沒用到的不用轉,也就是按需加載,因此咱們會看到那麼多的require語句。

還有一種方法是,不須要在每一個文件都進行轉換,由於這樣會致使代碼重複,同一個API在不一樣的文件都被轉換了。

那麼咱們只須要在入口文件中手動引入polyfill便可。以下:

import '@babel/polyfill'
複製代碼

polyfill後,咱們就能夠使用PromiseWeakMapArray.fromObject.assignArray.prototype.includes等方法了。

有種場景下,咱們可能不須要使用這麼多方法,好比在編寫庫或者工具的時候,那咱們就能夠使用transform-runtime來取代@babel/polyfill了。

五、Babel的配置形式

5-一、babel.config.js

項目根目錄下新建babel.config.js,內容以下:

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

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

  return {
    presets,
    plugins
  };
}
複製代碼

具體配置,可參考,這裏

5-二、.babelrc

項目根目錄下新建.babelrc,內容以下:

{
  "presets": [...],
  "plugins": [...]
}
複製代碼

具體配置,可參考,這裏

5-三、package.json

也能夠在package.json文件中配置

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}
複製代碼

5-四、.babelrc.js

這種方法和.babelrc配置的惟一區別就是,能夠編寫js代碼,好比:

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

if (process.env["ENV"] === "prod") {
  plugins.push(...);
}

module.exports = { presets, plugins };
複製代碼

5-五、cli中的配置

babel --plugins @babel/plugin-transform-arrow-functions script.js
複製代碼

具體配置,可參考https://babeljs.io/docs/en/babel-cli

5-六、核心包中的配置

require("@babel/core").transform("code", {
  plugins: ["@babel/plugin-transform-arrow-functions"]
});
複製代碼

具體配置,可參考,這裏

六、plugin

6-一、插件的寫法

插件的寫法:

  1. 直接寫插件名稱
{
  "plugins": ["babel-plugin-myPlugin"]
}
複製代碼
  1. 直接寫插件路徑,能夠是相對路徑也能夠是絕對路徑
{
  "plugins": ["./node_modules/asdf/plugin"]
}
複製代碼
  1. 插件縮寫,若是插件是以babel-plugin-開頭的,就能夠縮寫
{
  "plugins": [
    "myPlugin",
    "babel-plugin-myPlugin" // equivalent
  ]
}
複製代碼

6-二、插件的順序

幾條規則:

    1. 插件在預設以前工做,也就是先pluginpreset
    1. plugin的順序是從左到右。first to last
    1. preset的順序是從右到左。last to first

preset的順序爲啥是這樣的,官方是這樣說的:

This is mostly for ensuring backwards compatibility, since most users list "es2015" before "stage-0"。

6-三、給插件傳參數

插件的寫法有如下三種,第三種就是傳遞參數的形式

{                 1           2              3
  "plugins": ["pluginA", ["pluginA"], ["pluginA", {}]]
}

複製代碼

具體一點就是這樣:

{
  "plugins": [
    [
      "transform-async-to-module-method",
      {
        "module": "bluebird",
        "method": "coroutine"
      }
    ]
  ]
}
複製代碼

preset也同樣:

{
  "presets": [
    [
      "env",
      {
        "loose": true,
        "modules": false
      }
    ]
  ]
}
複製代碼

6-四、編寫插件

一個插件長這樣:

export default function() {
  return {
    visitor: {
      Identifier(path) {
        const name = path.node.name;
        // reverse the name: JavaScript -> tpircSavaJ
        path.node.name = name
          .split("")
          .reverse()
          .join("");
      },
    },
  };
}
複製代碼

具體可參考babel手冊

相關文章
相關標籤/搜索