主頁 > 企業開發 > 你必須要知道的babel二三事

你必須要知道的babel二三事

2020-09-14 13:13:35 企業開發

1. 什么是babel

本文基于的babel版本是7.11.6,本文所有示例github

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

Babel是一個工具鏈,主要用于將ECMAScript 2015+代碼轉換為當前和較老的瀏覽器或環境中的向后兼容的JavaScript版本,

1.1 我們能用bebel做什么?

  • 針對于新出的ECMAScript標準,部分瀏覽器還不能完全兼容,需要將這部分語法轉換為瀏覽器能夠識別的語法,比如有些瀏覽器不能正常決議es6中的箭頭函式,那通過babel轉換后,就能將箭頭函式轉換為瀏覽器能夠“認懂”得語法,

  • 針對于一些較老的瀏覽器,比如IE10或者更早之前,對一些最新的內置物件Promise/Map/Set,靜態方法Arrary.from/Object.assign以及一些實體方法Array.prototype.includes,這些新的特性都不存在與這些老版本的瀏覽器中,那么就需要給這些瀏覽器中的原始方法中添加上這些特性,即所謂的polyfill

  • 可以做一些原始碼的轉換,即可以直接使用babel中提供的API對代碼進行一些分析處理,例如

    •   const filename = 'index.js'
        const { ast } = babel.transformSync(source, { filename, ast: true, code: false });
        const { code, map } = babel.transformFromAstSync(ast, source, {
            filename,
            presets: ["minify"],
            babelrc: false,
            configFile: false,
        });
      

2. 使用babel

下面講到的幾種轉換方式,其實本質上都是一樣的,都是呼叫babel-core中的API來進行直接轉換

2.1 使用babel.transform直接轉換

const source = `
const someFun = () => {
    console.log('hello world');
}
`;

require("@babel/core").transform(source, {
  plugins: ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-parameters"],
}, result => {
  console.log(result.code);
});

2.1 使用babel-cli

babel提供了cli的方式,可以直接讓我們使用命令列的方式來使用babel,具體參照一下做法

## install
## 首先需要安裝 @babel/core @babel/cli
## @babel/cli是提供的命令列工具,會內部呼叫@babel/core來進行代碼轉換
npm install @babel/core @babel/cli --save-dev

## usage
npx babel ./cli/index.js

本地安裝完依賴后,就可以使用babel來進行代碼轉換了,npx babel [options] files,babel提供了一些常用的cli命令,可以使用npx babel --help來查看

> $ npx babel --help                                                                                                           ? 12.13.0 [±master ●●●]
Usage: babel [options] <files ...>

Options:
  -f, --filename [filename]                   The filename to use when reading from stdin. This will be used in source-maps, errors etc.
  --presets [list]                            A comma-separated list of preset names.
  --plugins [list]                            A comma-separated list of plugin names.
  --config-file [path]                        Path to a .babelrc file to use.
  --env-name [name]                           The name of the 'env' to use when loading configs and plugins. 																							 Defaults to the value of BABEL_ENV, or else NODE_ENV, or else 
                                              'development'.

下面是一個簡單的例子,比如有這么一段源代碼,

// cli/index.js

const arrayFn = (...args) => {
    return ['babel cli'].concat(args);
}

arrayFn('I', 'am', 'using');

執行以下命令:npx babel ./cli/index.js --out-file ./cli/index.t.js,結果如下圖:

babel-cli-1

代碼和源代碼竟然是一模一樣的,為什么箭頭函式沒有進行轉換呢?這里就會引入plugins以及preset的概念,這里暫時不會具體講解,只需要暫時知道,代碼的轉換需要使用plugin進行,

轉換箭頭函式,我們需要使用到@babel/plugin-transform-arrow-functions/parameters,首先安裝完之后,在此執行轉換

npm install @babel/plugin-transform-arrow-functions @babel/plugin-transform-parameters --save-dev
npx babel ./cli/index.js --out-file ./cli/index.t.js --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-parameters

執行完之后,再看生成的檔案

babel-cli-2

2.3 使用webpack babel-loader來進行轉換

創建webpack.config.js,撰寫如下配置

// install
npm install webpack-cli --save-dev

// webpack/webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'index.bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-parameters"]
          }
        }
      }
    ]
  }
};

// usage
cd webpack
npx webpack

可以得到轉換之后的代碼如下:

webpack-1

可以對比查看babel-cli的轉換之后的代碼是一致的,

2.4 使用組態檔來進行轉換

參看以上三種方式,都必須加載了plugins這個引數選項,尤其是在cli方式中,如果需要加載很多插件,是非常不便于書寫的,同時,相同的配置也不好移植,比如需要在另外一個專案中同樣使用相同的cli執行,那么顯然插件越多,就會越容易出錯,鑒于此,babel提供了config的方式,類似于webpack的cli方式以及config方式,

babel在7.0之后,引入了babel.config.[extensions],在7.0之前,專案都是基于.babelrc來進行配置,這里暫時不會講解它們之間的區別,下面就是一個比較基于上面例子的一個.babelrc檔案,

// .babelrc
{
  "plugins": ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-parameters"]
}

我們將這個檔案放置在根目錄下,新建一個config的檔案夾,從cli目錄中將index.js檔案copy到config目錄下,然后執行npx babel ./config/index.js --out-file ./config/index.t.js,完成之后,會發現和cli執行的方式并沒有什么差別,

3. babel.config.json vs .babelrc

babel.config.js是在babel第7版引入的,主要是為了解決babel6中的一些問題,參看https://babeljs.io/docs/en/config-files#6x-vs-7x-babelrc-loading

  • .babelrc會在一些情況下,莫名地應用在node_modules中
  • .babelrc的配置不能應用在使用符號鏈接參考進來的檔案
  • 在node_modules中的.babelrc會被檢測到,即使它們中的插件和預設通常沒有安裝,也可能在Babel編譯檔案的版本中無效

3.1 .babelrc在monorepo專案中的一些問題

另外如果只使用.babelrc,在monorepo專案中會遇到一些問題,這得從.babelrc加載的兩條規則有關

  • 在向上搜索配置的程序中,一旦在檔案夾中找到了package.json,就會停止搜尋其它配置(babel用package.json檔案來劃定package的范圍)
  • 這種搜索行為找到的配置,如.babelrc檔案,必須位于babel運行的root目錄下,或者是包含在babelrcRoots這個option配置的目錄下,否則找到的配置會直接被忽略

下面我們在之前的例子上進行改造,檔案結構如下:

babelrc-1

在mod1檔案夾中創建一個package.json檔案,內容為{},現在執行以下代碼:

npx babel ./config/mod1/index.js -o ./config/mod1/index.t.js

可以發現,index.js沒有編譯,因為在向上查找的時候,找到了mod1中的package.json,但是在此目錄中并沒有找到.babelrc檔案,因此不會編譯,

下面,我們將.babelrc檔案移至mod1中,然后再執行上面的命令,這次會編譯成功么?

babelrc-2

答案依舊是不會,因為當前的執行目錄是在src下面,所以在mod1目錄中的組態檔將會被忽略掉,

這里有兩種方法來解決這個問題:

  • 進入到mod1目錄中直接執行 cd ./config/mod1 & npx babel index.js -o index.t.js

  • 在執行的root目錄下,添加一個babel.config.json檔案,在其中添加babelrcRoots將這個目錄添加進去

babelrc-3

然后再執行npx babel ./config/mod1/index.js -o ./config/mod1/index.t.js就可以正常編譯了,

正是基于上述的一些問題,babel在7.0.0之后,引入了babel.config.[json/js/mjs/cjs],基于babel.config.json的配置會靈活得多,

3.2 Project-wide configuration

一般babel.config.json會放置在根目錄下,在執行編譯時,babel會首先去尋找babel.config.json檔案,以此來作為整個專案的根配置,

如果在子目錄中不存在.babelrc的配置,那么在編譯時,會根據根目錄下的配置來進行編譯,比如在config/index.js中添加如下代碼

babel-config-1

執行npx babel ./config/index -o ./config/index.t.js后會發現for..of這段代碼會被原樣輸出,因為在config目錄中并沒有針對for..of配置插件,現在在config檔案中添加.babelrc,內容如下:

{
  "plugins": [
    "@babel/plugin-transform-for-of"
  ]
}

再次執行完,會發現,for..of會被babel編譯

babel-config-2

說明,如果子檔案夾中存在相應的babel配置,那么編譯項會在根配置上進行擴展,

但這點在monorepo專案中會有點例外,之前我在mod1檔案家中放置了一個package.json檔案:

babel-config-3

執行下面命令

npx babel ./config/mod1/index.js -o ./config/mod1/index.t.js

發現for..of部分并沒有被babel編譯,這個原因和之前在講bablerc的原因是一樣的,因為執行的根目錄是src,因此在mod1中并不能去加載.babelrc配置,因此只根據根目錄中的配置來執行編譯,想要mod1中的配置也被加載,可以按照相同的方法在babel.config.json中配置babelrcRoots

另外如果子檔案家中不存在相應的配置,比如在cli目錄下,在src目錄下執行config/index.js檔案是沒有問題的,但是如果進入cli中,然后直接執行,會發現index.js檔案不會被編譯,由此,你需要告訴babel去找到這個配置,這里可以使用rootMode: upward來使babel向上查找babel.config.json,并以此作為根目錄,

cd cli & npx babel ./index.js -o ./index.t.js --root-mode upward

3.3 推薦使用場景

  • babel.config.json
    • 你正在使用一個monorepo(可以理解為在一個專案中會有多個子工程)
    • 你希望編譯node_modules以及symobllinked-project中的代碼
  • .babelrc
    • 你的配置僅適用于專案的單個部分
    • 需要在子目錄/檔案中運行一些特定的轉換,比如你可能不希望一些第三方庫被轉碼
  • 綜合推薦使用babel.config.json,Babel itself is using it

4. plugins & Presets

Babel is a compiler (source code => output code). Like many other compilers it runs in 3 stages: parsing, transforming, and printing.

Now, out of the box Babel doesn't do anything. It basically acts like const babel = code => code; by parsing the code and then generating the same code back out again. You will need to add plugins for Babel to do anything.

沒有plugins,babel將啥事也做不了,

babel提供了豐富的插件來對不同時期的代碼進行轉換,例如我們在es6最常使用的箭頭函式,當需要轉化為es5版本時,就用到了arrow-functions這個插件,

具體的插件串列,可以查看plugins,

presets的中文翻譯為預設,即為一組插件串列的集合,我們可以不必再當獨地一個一個地去添加我們需要的插件,比如我們希望使用es6的所有特性,我們可以使用babel提供的ES2015這個預設,

4.1 基本用法

// 如果plugin已經在發布到npm中
// npm install @babel/plugin-transform-arrow-functions -D
// npm install @babel/preset-react -D
{
  "plugins": ["@babel/plugin-transform-arrow-functions"],
  "presets": ["@babel/preset-react"]
}

// 或者按照babel的規范,引入自己撰寫的plugin/preset
{
  "plugins": ["path/to/your/plugin"],
  "presets": ["path/to/your/preset"],
}

4.2 選項

任何一個插件都可以擁有自定義的屬性來定義這個插件的行為,具體的寫法可以為:

{
  "plugins": ["pluginA", ["pluginA"], ["pluginA", {}]],
  "presets": ["presetA", ["presetA"], ["presetA", {}]]
}

// example
{
  "plugins": [
    [
      "@babel/plugin-transform-arrow-functions",
      { "spec": true }
    ]
  ],
  "presets": [
    [
      "@babel/preset-react",
      {
        "pragma": "dom", // default pragma is React.createElement (only in classic runtime)
        "pragmaFrag": "DomFrag", // default is React.Fragment (only in classic runtime)
        "throwIfNamespace": false, // defaults to true
        "runtime": "classic" // defaults to classic
        // "importSource": "custom-jsx-library" // defaults to react (only in automatic runtime)
      }
    ]
  ]
}

4.3 執行順序

  • 插件執行順序在presets之前
  • 插件會按照宣告的插件串列順序順序執行(first to last)
  • preset會按照宣告的串列順序逆序執行(last to first)

4.3.1 plugin的執行順序測驗

下面我們來做幾個例子測驗一下,首先,官方給出的插件標準寫法如下(在此之前,強烈建議閱讀babel-handbook來了解接下來插件編碼中的一些概念):

// 1. babel使用babylon將接受到的代碼進行決議,得到ast樹,得到一系列的令牌流,例如Identifier就代表一個字
// 符(串)的令牌
// 2. 然后使用babel-traverse對ast樹中的節點進行遍歷,對應于插件中的vistor,每遍歷一個特定的節點,就會給visitor添加一個標記
// 3. 使用babel-generator對修改過后的ast樹重新生成代碼

// 下面的這個插件的主要功能是將字串進行反轉
// plugins/babel-plugin-word-reverse.js
module.exports = function() {
  return {
    visitor: {
      Identifier(path) {
        console.log("word-reverse plugin come in!!!");
        const name = path.node.name;
        path.node.name = name
          .split("")
          .reverse()
          .join("");
      },
    },
  };
}

// 然后我們再提供一個插件,這個插件主要是修改函式的回傳值
// plugins/babel-plugin-replace-return.js
module.exports = function({ types: t }) {
  return {
    visitor: {
      ReturnStatement(path) {
        console.log("replace-return plugin come in!!!");
        path.replaceWithMultiple([
         t.expressionStatement(t.stringLiteral('Is this the real life?')),
         t.expressionStatement(t.stringLiteral('Is this just fantasy?')),
         t.expressionStatement(t.stringLiteral('(Enjoy singing the rest of the song in your head)')),
       ]);
      },
    },
  };
}

首先我們來測驗一下原始代碼是否通過我們自定義的插件進行轉換了,源代碼如下:

// plugins/index.js
const myPluginTest = (javascript) => {
  return 'I love Javascript';
}

// 然后在plugins目錄下創建一個.babelrc檔案,用于繼承默認的babel.config.json檔案
// plugins/.babelrc
{
  "plugins": ["./babel-plugin-word-reverse", "./babel-plugin-replace-return"]
}

// usage
npx babel ./plugins/index.js -o ./plugins/index.t.js

以下是執行完之后的結果

babel-plugin-1

從截圖可以看出,字串被反轉了,以及回傳的字串也被替換掉了,

然后我們再來看看執行的順序

babel-plugin-2

可以看到,排在插件串列之前的插件會在提前執行,

4.3.2 preset的執行順序測驗

下面再新建一個插件,用于自定義的preset撰寫

// presets/babel-plugin-word-replace.js
// 這個插件主要的功能是給每個節點型別為Identifier的名稱拼接一個_replace的后綴
module.exports = function() {
  return {
    visitor: {
      Identifier(path) {
        console.log("word-replace plugin come in!!!");
        let name = path.node.name;
        path.node.name = name += '_replace';
      },
    },
  };
}

然后我們借助之前撰寫的babel-plugin-word-reverse來撰寫兩個新的presets

// presets/my-preset-1.js
module.exports = () => {
  console.log('preset 1 is executed!!!');
  return {
    plugins: ['../plugins/babel-plugin-word-reverse']
  };
};

// presets/my-preset-2.js
module.exports = () => {
  console.log('preset 2 is executed!!!');
  return {
    presets: ["@babel/preset-react"],
    plugins: ['./babel-plugin-word-replace', '@babel/plugin-transform-modules-commonjs'],
  };
};

// 創建.babelrc配置
// presets/.babelrc
{
  "presets": [
    "./my-preset-1",
    "./my-preset-2"
  ]
}

// 測驗代碼
// presets/index.jsx
import React from 'react';

export default () => {
  const text = 'hello world';
  return <div>{text}</div>;
}

// 執行
npx babel ./presets/index.jsx -o ./presets/index.t.js

可以看到在.babelrc中,將preset-1放在了preset-2的前面,如果按照babel官網給出的決議,那么preset2會被先執行,執行的順序如下

babel-preset-1

可以看到控制臺列印的順序是preset1 -> preset2,這點與官網給出的preset執行順序是相反的???

然后再看編譯之后生成的檔案,發現竟然又是先執行了preset-2中的插件,然后在執行preset-1中的插件,如圖:

babel-preset-2

可以看到顯然是首先經過了添加后綴_replace,然后在進行了整體的reverse,這里是不是意味著,在presets串列中后宣告的preset中的插件會先執行呢???

懷著這個問題,去啃了下源代碼,發現babel所說的執行順序,其實是traverse訪問插件中vistor的順序,因為presets其實也是一組插件的集合,經程序式處理之后,會使得presets末尾的plugins會出現在整個plugins串列的前面,

同時可以看圖中控制臺的列印結果,word-replace始侄訓在word-reverse之前,并且是成對出現的,

// babel/packages/babel-core/src/transform.js [line 21]

const transformRunner = gensync<[string, ?InputOptions], FileResult | null>(
  function* transform(code, opts) {
    const config: ResolvedConfig | null = yield* loadConfig(opts);
    if (config === null) return null;

    return yield* run(config, code);
  },
);

loadConfig(opts)會被傳遞進來的plugins以及presets進行處理,進去看看發生了什么?

// babel/packages/babel-core/src/config/full.js [line 59]

export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
  inputOpts: mixed,
): Handler<ResolvedConfig | null> {
  const result = yield* loadPrivatePartialConfig(inputOpts);

	// ...
	const ignored = yield* (function* recurseDescriptors(config, pass) {
    const plugins: Array<Plugin> = [];
    for (let i = 0; i < config.plugins.length; i++) {
      const descriptor = config.plugins[i];
      if (descriptor.options !== false) {
        try {
          plugins.push(yield* loadPluginDescriptor(descriptor, context));
        } catch (e) {
          // ...
        }
      }
    }

    const presets: Array<{|
      preset: ConfigChain | null,
      pass: Array<Plugin>,
    |}> = [];
    for (let i = 0; i < config.presets.length; i++) {
      const descriptor = config.presets[i];
      if (descriptor.options !== false) {
        try {
          presets.push({
            preset: yield* loadPresetDescriptor(descriptor, context),
            pass: descriptor.ownPass ? [] : pass,
          });
        } catch (e) {
          // ...
        }
      }
    }

    // resolve presets
    if (presets.length > 0) {
      // ...

      for (const { preset, pass } of presets) {
        if (!preset) return true;

        const ignored = yield* recurseDescriptors(
          {
            plugins: preset.plugins,
            presets: preset.presets,
          },
          pass,
        );
        // ...
      }
    }

    // resolve plugins
    if (plugins.length > 0) {
      pass.unshift(...plugins);
    }
  })(//...)
}

loadPrivatePartialConfig中會依次執行我們定義的plugins以及presets,這也是為什么在上面的例子中preset1會列印在preset2,

// babel/packages/babel-core/src/config/config-chain.js [line 629]

function mergeChainOpts(
  target: ConfigChain,
  { options, plugins, presets }: OptionsAndDescriptors,
): ConfigChain {
  target.options.push(options);
  target.plugins.push(...plugins());
  target.presets.push(...presets());

  return target;
}

recurseDescriptors這里是一個遞回函式,是用來在passes中存放決議過后的plugins以及presets的,passes通過unshift的方式決議每次回圈之后的插件,因此presets的回圈越靠后,在passes中的plugins反而會越靠前,這也是為什么presets串列中的執行順序是逆序的原因,

// babel/packages/babel-core/src/config/full.js [line 195]

opts.plugins = passes[0];
opts.presets = passes
  .slice(1)
  .filter(plugins => plugins.length > 0)
  .map(plugins => ({ plugins }));
opts.passPerPreset = opts.presets.length > 0;

return {
  options: opts,
  passes: passes,
};

設定決議后的plugins,然后回傳新的config,

5. polyfill

Babel 7.4.0之后,@babel/polyfill這個包已經廢棄了,推薦直接是用core-js/stable以及regenerator-runtime/runtime

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

polyfill的直接翻譯為墊片,是為了添加一些比較老的瀏覽器或者環境中不支持的新特性,比如Promise/ WeakMap,又或者一些函式Array.form/Object.assign,以及一些實體方法Array.prototype.includes等等,

注意:這些新的特性會直接加載全域的環境上,在使用時請注意是否會污染當前的全域作用域

5.1 基本使用

npm install --save @babel/polyfill

// commonJs
require('@babel/polyfill')

// es6
import('@babel/polyfill')

當在webpack中使用時,官方推薦和@babel/preset-env一起使用,因為這個preset會根據當前配置的瀏覽器環境自動加載相應的polyfill,而不是全部進行加載,從而達到減小打包體積的目的

// .bablerc
{
  "presets": [
    [
      "@babel/preset-env", {
        "useBuiltIns": "usage", // 'entry/false'
        "corejs": 3
      }
    ]
  ]
}

useBuiltIns有三個選項

  • usage 當使用此選項時,只需要安裝@babel-polyfill即可,不需要在webpack中引入,也不需要在入口檔案中引入(require/import)

  • entry 當使用此選項時,安裝完@babel-polyfill之后,然后在專案的入口檔案中引入

  • false 當使用此選項時,需要安裝依賴包,然后加入webpack.config.js的entry中

    module.exports = {
      entry: ["@babel/polyfill", "./app/js"],
    };
    

在瀏覽器中使用,可以直接引入@bable/polyfill中的dist/polyfill.js

<script src='https://www.cnblogs.com/rynxiao/p/dist/polyfill.js'></script>

5.2 示例

通過配合使用@babel/preset-env之后,我們可以來看看編譯之后生成了什么?

// polyfill/.babelrc
{
  "presets": [
    [
      "@babel/preset-env", {
        "useBuiltIns": "usage", 	// 其他兩個選項 'entry/false'
        "corejs": 3 							// 如果需要使用includes,需要安裝corejs@3版本
      }
    ]
  ]
}

// polyfill/index.js
const sym = Symbol();

const promise = Promise.resolve();

const arr = ["arr", "yeah!"];
const check = arr.includes("yeah!");

console.log(arr[Symbol.iterator]());

編譯之后的結果如下

polyfill-1

可以看到,瀏覽器中缺失的方法、物件都是直接引入的,當你只需要在特定的瀏覽器中做兼容時,可以顯式地宣告,使用方式可以參照browserslist-compatible,

{
  "targets": "> 0.25%, not dead",
  
  // 或者指明特定版本
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}

6. transform-runtime

A plugin that enables the re-use of Babel's injected helper code to save on codesize.

@babel/plugin-transform-runtime的主要有三個用處

  • 自動引入@babel/runtime/regenerator,當你使用了generator/async函式(通過regenerator選項打開,默認為true)
  • 提取一些babel中的工具函式來達到減小打包體積的作用
  • 如果開啟了corejs選項(默認為false),會自動建立一個沙箱環境,避免和全域引入的polyfill產生沖突,

這里說一下第三點,當開發自己的類別庫時,建議開啟corejs選項,因為你使用的polyfill可能會和用戶期待的產生沖突,一個簡單的比喻,你開發的類別庫是希望兼容ie11的,但是用戶的系統是主要基于chorme的,根本就不要去兼容ie11的一些功能,如果交給用戶去polyfill,那就的要求用戶也必須要兼容ie11,這樣就會引入額外的代碼來支持程式的運行,這往往是用戶不想看到的,

6.1 基本使用

// dev dependence
npm install --save-dev @babel/plugin-transform-runtime

// production dependence
// 因為我們需要在生產環境中使用一些runtime的helpers
npm install --save @babel/runtime

// .babelrc
// 默認配置
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": false,
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
        "version": "7.0.0-beta.0"
      }
    ]
  ]
}

6.2 示例

說了這么多,下面來看一個示例

// transform-runtime/.babelrc
{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "helpers": false
      }
    ]
  ]
}

// transform-runtime/index.js
const sym = Symbol();

const promise = Promise.resolve();

const arr = ["arr", "yeah!"];
const check = arr.includes("yeah!");

class Person {}

new Person();

console.log(arr[Symbol.iterator]());

這里暫時關閉了helpers,我們來看看編譯之后會是什么結果

transform-runtime-1

可以看到,編譯之后,將Person class生成了一個函式_classCallCheck,你可能覺得一個生成這樣的函式也沒什么特別大的關系,但是如果在多個檔案中都宣告了class,那就意味著,將會在多個檔案中生成一個這么一模一樣的工具函式,那么體積就會變大了,因此,開啟了helpers之后,效果又是怎樣的呢?

transform-runtime-2

可以看到,需要生成的方法變成了引入的方式,注意引入的庫是@babel-runtime

下面來試試開啟了corejs選項之后生成的檔案是啥樣的?

transform-runtime-3

可以看到所有的工具方式都來自于@babel/runtime-corejs2,因為是獨立于polyfill生成的,所以不會污染全域環境,

總結

  • 推薦使用babel.config.js來作為整個專案的babel配置,.babelrc更加適用于monorepo專案
  • babel的編譯基本都是依賴于plugin,preset是一組plugin的集合
  • polyfill為一些較老的瀏覽器提供一些新特性的支持
  • transform-runtime可以提取一些幫助函式來減小打包的體積,在開發自己的類別庫是,建議開啟corejs選項

參考鏈接

  • babeljs.io
  • babel詳解(七)-組態檔
  • Babel快速上手使用指南

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/35239.html

標籤:JavaScript

上一篇:JS事件回圈機制

下一篇:CSS選擇器的優先級

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more