Babel 入門指引

20200810223955

譯文:Babel.js Guide -Part 1- The Absolute Must-Know Basics: Plugins, Presets, And Config前端

本文將講述:
  • Babel 是什麼以及怎麼在平常開發中使用它?
  • presets and plugins 是什麼及區別,在babel執行中,他們的執行順序是什麼?

雖然原文標題看似是一個系列,但做者彷佛沒有繼續,但我已經想好了下一部分要寫的內容;非專業翻譯,夾帶本身理解,有小改動。node

Babel 是什麼

babel 是一個免費開源的JavaScript 編譯庫. 它根據你的配置將代碼轉化成各式各樣的JS代碼。react

最多見的使用方式就是將現代語法JavaScript es6+編寫的代碼 轉化成 es5,從而兼容更多的瀏覽器(特別是IE),下面以Babel 轉換es6 箭頭函數 爲 es5 函數的爲例。webpack

// The original code
const foo = () => console.log('hello world!');

轉移後git

// The code after babel transpilation
var foo = function foo() {
  return console.log('hello world!');
};

你能夠在這裏在線嘗試es6

使用Babel

在線Repl

這是使用Babel的最簡單方法。這也許不是一個很是實用的工具,可是是最快的測試或者實驗Babel如何工做的工具, 在線Repl地址github

構建庫

使用Babel的最流行方法是使用WebpackGulpRollup等構建庫進行打包構建。每一個方式都使用Babel做爲構建的一部分來實現本身的構建過程。web

好比,咱們最經常使用的webpack:chrome

{
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: ['@babel/plugin-transform-arrow-functions']
          }
        }
      }
    ]
  },
  ...
}

庫經常使用的構建工具:Rollupnpm

rollup.rollup({
  ...,
  plugins: [
    ...,
    babel({
      plugins: ['@babel/plugin-transform-arrow-functions']
    }),
    ...
  ],
  ...
}).then(...)

腳手架cli

除了依賴構建庫,也能夠直接用命令行依賴官方提供的@babel/cli 包來編譯NIIT的代碼:

# install the core of babel and it's cli interface
npm install @babel/core @babel/cli

# use the babel command line
babel script.js --out-file script-compiled.js

Babel Plugins

Babel是經過插件配置的。開箱即用,Babel不會更改您的代碼。沒有插件,它基本上是這樣的:

// parse -> generate, 大白話就是英翻中,中翻英
const babel = code => code;

經過Babel插件運行代碼,您的代碼將轉換爲新代碼,以下所示:

// parse -> do something -> generate, 大白話就是英翻中,添油加醋,中翻英
const babel = code => babelPlugin2(babelPlugin1(code));

Babel Presets

您能夠單獨添加Babel插件列表,可是一般更方便的方法是使用Babel presets。

Babel presets結合一系列插件的集合。傳遞給presets的選項會影響其聚合的插件, 這些選項將控制使用哪些插件以及這些插件的配置。

好比,咱們前面看到的@babelplugin-transform-arrow-functions插件是@babel/preset-env presets的一部分。

@babel/preset-env多是最受歡迎的presets。 它根據用戶傳遞給預設的配置(好比browsers:目標瀏覽器/環境), 將現代JavaScript(即ES Next)轉換爲較舊的JavaScript版本。

好比:它能夠將()=> arrowFunctions,{…detructuring} 和class {}轉換爲舊版瀏覽器支持的JavaScript語法,舉個🌰, 目標瀏覽器爲IE11:

// 新版語法
class SomeClass {
  constructor(config){
    this.someFunction = params => {
      console.log('hello world!', {...config, ...params});
    }
  }
  someMethod(methodParams){
    this.someFunction(methodParams);
  }
  someOtherMethod(){
    console.log('hello some other world');
  }
}

編譯後:

// explained here: https://www.w3schools.com/js/js_strict.asp
"use strict";
// this is a babel helper function injected by babel to mimic a {...destructuring} syntax
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; }
// this is a babel helper function injected by babel for a faster-than-native property defining on an object
// very advanced info can be found here:
// https://github.com/babel/babel/blob/3aaafae053fa75febb3aa45d45b6f00646e30ba4/packages/babel-helpers/src/helpers.js#L348
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; }

// this is a babel helper function that makes sure a class is called with the "new" keyword like "new SomeClass({})" and not like "SomeClass({})"
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// like "_defineProperty" above, but for multiple props
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); } }

// used by babel to create a class with class functions 
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var SomeClass =
/*#__PURE__*/ // this marks the function as pure. check https://webpack.js.org/guides/tree-shaking/ for more info.
function () {
  // the class got converted to a function
  function SomeClass(config) {
    // make sure a class is called with the "new" keyword
    _classCallCheck(this, SomeClass);
    // someFunction is set on SomeClass 
    this.someFunction = function (params) {   
      // notice how the {...config, ...params} became _objectSpread({}, config, params) here
      console.log('hello world!', _objectSpread({}, config, params));
    };
  }
  // this function adds the class methods to the transpiled class created above
  _createClass(SomeClass, [
    {
      key: "someMethod",
      value: function someMethod(methodParams) {
        this.someFunction(methodParams);
      }
    },
    {
      key: "someOtherMethod",
      value: function someOtherMethod() {
        console.log('hello some other world');
      }
    }
  ]);
  return SomeClass;
}();

對於更復雜的構建要求,配置將使用項目根目錄中的babel.config.js文件。因爲是JavaScript文件,所以比.babelrc更加靈活。例如:

module.exports = function (api) {
  
  // Only execute this file once and cache the resulted config object below for the next babel uses.
  // more info about the babel config caching can be found here: https://babeljs.io/docs/en/config-files#apicache
  api.cache.using(() => process.env.NODE_ENV === "development")
  return {
    presets: [
      // Use the preset-env babel plugins
      '@babel/preset-env'
    ],
    plugins: [
      // Besides the presets, use this plugin
      '@babel/plugin-proposal-class-properties'
    ]
  }
}

@babel/preset-env不一樣配置,編譯出的代碼可能大不同,好比當配置爲:latest 10 Chrome versions是,上面一段代碼編譯結果與編譯前一致,由於上面的特性,chrrome都支持;但若是將10調整爲30,40時,你會發現,編譯的代碼將會愈來愈多;能夠點擊這裏嘗試一下

配置

Babel Plugins and Presets 是很是重要的概念,Babel的配置由Plugins and Presets組合而成(也可使用其餘幾個高級屬性);

簡單的配置,能夠直接使用.babelrc,babelrc是一種JSON5文件(和JSON同樣,但其容許註釋),被放置在項目根目錄下,好比下面這樣:

// Comments are allowed as opposed to regular JSON files
{
  presets: [
    // Use the preset-env babel plugins
    '@babel/preset-env'
  ],
  plugins: [
    // Besides the presets, use this plugin
    '@babel/plugin-proposal-class-properties'
  ]
}

對於更復雜的配置,通常使用babel.config.js文件來代替.babelrc文件,由於他是js文件,因此比.babelrc配置更靈活,舉個例子:

module.exports = function (api) {
  // Only execute this file once and cache the resulted config object below for the next babel uses.
  // more info about the babel config caching can be found here: https://babeljs.io/docs/en/config-files#apicache
  api.cache.using(() => process.env.NODE_ENV === "development")
  return {
    presets: [
      // Use the preset-env babel plugins
      '@babel/preset-env'
    ],
    plugins: [
      // Besides the presets, use this plugin
      '@babel/plugin-proposal-class-properties'
    ]
  }
  
}

一些配置文件可能很是複雜, 例如: Babel項目自己的babel.config.js。莫慌!閱讀本指南系列後,您將知道此複雜配置的每一行的意義(看,有頭牛在天上飛)。

Babel Plugins and Presets 執行順序

若是您在配置中混合使用了Plugins和Presets,Babel將按如下順序應用它們:

  • 首先從上到下應用插件;
  • 而後,將預設應用在插件以後,從下到上;

舉個🌰:

{
  presets: [
    '@babel/preset-5', //   ↑    ** End Here ** Last preset to apply it's plugins *after* all the plugins below finished to run 
    '@babel/preset-4', //   ↑
    '@babel/preset-3', //   ↑    2
    '@babel/preset-2', //   ↑
    '@babel/preset-1', //   ↑    First preset to apply it's plugins *after* all the plugins below finished to run 
  ],
  plugins: [
    '@babel/plugin-1', //   ↓    >>> Start Here <<< First plugin to transpile the code.
    '@babel/plugin-2', //   ↓  
    '@babel/plugin-3', //   ↓    1
    '@babel/plugin-4', //   ↓  
    '@babel/plugin-5', //   ↓    Last plugin to transpile the code before the preset plugins are applied
  ]
}

另外,值得一提的是,每一個Presets中的插件也自上而下應用。

以正確的順序配置Plugins和Presets很是重要! 正確的順序可能會加快翻譯速度,錯誤的順序可能會產生不須要的結果或者致使錯誤。

可能上面的🌰不夠真實,那就來個真實的吧:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ]
}

decorators 是裝飾器語法,如今還處於stage-3階段難產,而JSX則是React的專有語法;若是沒有@babel/plugin-proposal-decorators@babel/preset-react 先編譯,直接運行@babel/preset-env編譯,就會報@<div> 無效的語法標識,全部正確的配置插件和插件順序是多麼重要。

Babel Plugins and Presets 選項

上面提到過給@babel/preset-env設置不一樣的browsers選項,會獲得不一樣的編譯結果;經過將選項包裝在數組中並向其中添加選項,能夠將選項配置傳遞給Babel Plugins and Presets,好比位於@babel/preset-env後面的對象就是一個選項配置,告訴編譯的目標是兼容到chrome 58版本和IE11:

{
  presets: [
    // Notice how @babel/preset-env is wrapped in an array with an options object
    ['@babel/preset-env', {
      "targets": {
        "chrome": "58",
        "ie": "11"
      }
    }],
    '@babel/some-other-preset'
  ]
}

@babel/preset-env基本是項目編譯必選的Presets,除了targets,還有useBuiltIns,esmodules,modules等常見選項,還有更多關於配置可參考官網

最後

關於指引,就這麼多,將在不久後推出進階篇, 關於babel-runtime與babel-polyfill。

公衆號:前端黑洞

相關文章
相關標籤/搜索