Babel7 轉碼(四)- polyfill 仍是 transform-runtime

@babel/polyfill 仍是 @babel/plugin-transform-runtime ?

webpack 搭建文檔:https://webpack.eleven.net.cnhtml

首先要確認,@babel/polyfill 和 @babel/plugin-transform-runtime 各自均可以完成 ES 新 API 的轉譯,ES 新語法是由 @babel/preset-env 完成轉譯,因此,@babel/polyfill、@babel/plugin-transform-runtime 都需各自搭配 @babel/preset-env 一塊兒使用。翻閱、參考了大量資料,並在實際生產開發中驗證,兩者的配置、原理總結以下:
  1. @babel/preset-env + @babel/polyfill能夠轉譯語法、新 API,但存在污染全局問題;
  2. @babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs2,可按需導入,轉譯語法、新 API,且避免全局污染(babel7 中@babel/polyfill 是@babel/runtime-corejs2 的別名),可是檢測不到‘hello‘.includes(‘h‘)這種句法;
  3. @babel/polyfill 和@babel/runtime-corejs2 都使用了 core-js(v2)這個庫來進行 api 的處理。
    core-js(v2)這個庫有兩個核心的文件夾,分別是 library 和 modules。@babel/runtime-corejs2 使用 library 這個文件夾,@babel/polyfill 使用 modules 這個文件夾。前端

    1. library 使用 helper 的方式,局部實現某個 api,不會污染全局變量;
    2. modules 以污染全局變量的方法來實現 api;
    3. library 和 modules 包含的文件基本相同,最大的不一樣是_export.js 這個文件:node

      moduleswebpack

      // core-js/modules/_exports.js
      var global = require('./_global');
      var core = require('./_core');
      var hide = require('./_hide');
      var redefine = require('./_redefine');
      var ctx = require('./_ctx');
      var PROTOTYPE = 'prototype';
      
      var $export = function (type, name, source) {
        var IS_FORCED = type & $export.F;
        var IS_GLOBAL = type & $export.G;
        var IS_STATIC = type & $export.S;
        var IS_PROTO = type & $export.P;
        var IS_BIND = type & $export.B;
        var target = IS_GLOBAL ? global : IS_STATIC ? global[name] || (global[name] = {}) : (global[name] || {})[PROTOTYPE];
        var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
        var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {});
        var key, own, out, exp;
        if (IS_GLOBAL) source = name;
        for (key in source) {
          // contains in native
          own = !IS_FORCED && target && target[key] !== undefined;
          // export native or passed
          out = (own ? target : source)[key];
          // bind timers to global for call from export context
          exp = IS_BIND && own ? ctx(out, global) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
          // extend global
          if (target) redefine(target, key, out, type & $export.U);
          // export
          if (exports[key] != out) hide(exports, key, exp);
          if (IS_PROTO && expProto[key] != out) expProto[key] = out;
        }
      };
      global.core = core;
      // type bitmap
      $export.F = 1;   // forced
      $export.G = 2;   // global
      $export.S = 4;   // static
      $export.P = 8;   // proto
      $export.B = 16;  // bind
      $export.W = 32;  // wrap
      $export.U = 64;  // safe
      $export.R = 128; // real proto method for `library`
      module.exports = $export;

      libraryes6

      // core-js/library/_exports.js
      var global = require('./_global');
      var core = require('./_core');
      var ctx = require('./_ctx');
      var hide = require('./_hide');
      var has = require('./_has');
      var PROTOTYPE = 'prototype';
      
      var $export = function (type, name, source) {
        var IS_FORCED = type & $export.F;
        var IS_GLOBAL = type & $export.G;
        var IS_STATIC = type & $export.S;
        var IS_PROTO = type & $export.P;
        var IS_BIND = type & $export.B;
        var IS_WRAP = type & $export.W;
        var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
        var expProto = exports[PROTOTYPE];
        var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE];
        var key, own, out;
        if (IS_GLOBAL) source = name;
        for (key in source) {
          // contains in native
          own = !IS_FORCED && target && target[key] !== undefined;
          if (own && has(exports, key)) continue;
          // export native or passed
          out = own ? target[key] : source[key];
          // prevent global pollution for namespaces
          exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
          // bind timers to global for call from export context
          : IS_BIND && own ? ctx(out, global)
          // wrap global constructors for prevent change them in library
          : IS_WRAP && target[key] == out ? (function (C) {
            var F = function (a, b, c) {
              if (this instanceof C) {
                switch (arguments.length) {
                  case 0: return new C();
                  case 1: return new C(a);
                  case 2: return new C(a, b);
                } return new C(a, b, c);
              } return C.apply(this, arguments);
            };
            F[PROTOTYPE] = C[PROTOTYPE];
            return F;
          // make static versions for prototype methods
          })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
          // export proto methods to core.%CONSTRUCTOR%.methods.%NAME%
          if (IS_PROTO) {
            (exports.virtual || (exports.virtual = {}))[key] = out;
            // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%
            if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out);
          }
        }
      };
      // type bitmap
      $export.F = 1;   // forced
      $export.G = 2;   // global
      $export.S = 4;   // static
      $export.P = 8;   // proto
      $export.B = 16;  // bind
      $export.W = 32;  // wrap
      $export.U = 64;  // safe
      $export.R = 128; // real proto method for `library`
      module.exports = $export;
    4. 能夠看出,library下的這個$export方法,會實現一個wrapper函數,防止污染全局變量。
    5. 例如對Promise的轉譯,@babel/polyfill和@babel/runtime-corejs2的轉譯方式差別以下:web

      var p = new Promise();
      
      // @babel/polyfill
      require("core-js/modules/es6.promise");
      var p = new Promise();
      
      // @babel/runtime-corejs2
      var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
      var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
      var a = new _promise.default();
    6. 從上面這個例子能夠看出,對於Promise這個api,@babel/polyfill引用了core-js/modules中的es6.promise.js文件,由於是對全局變量進行處理,因此賦值語句不用作處理;@babel/runtime-corejs2會生成一個局部變量_promise,而後把Promise都替換成_promise,這樣就不會污染全局變量了。
  • 綜合上面的分析,得出結論:api

    1. 若是是本身的應用: @babel/preset-env + @babel/polyfillpromise

      1. 根據useBuiltIns參數肯定如何使用@babel/polyfill,具體參數設置總結以下:安全

        1. useBuiltIns設置爲entry比較不錯,推薦使用。
          在js代碼第一行import '@babel/polyfill',或在webpack的入口entry中寫入模塊@babel/polyfill,會將browserslist環境不支持的全部墊片都導入;
          可以覆蓋到‘hello‘.includes(‘h‘)這種句法,足夠安全且代碼體積不是特別大!
        2. useBuiltIns設置爲usage
          項目裏不用主動import,會自動將代碼裏已使用到的、且browserslist環境不支持的墊片導入;
          相對安全且打包的js體積不大,可是,一般咱們轉譯都會排除node_modules/目錄,若是使用到的第三方包有個別未作好ES6轉譯,有遇到bug的可能性,而且檢測不到‘hello‘.includes(‘h‘)這種句法。
          代碼書寫規範,且信任第三方包的時候,可使用!
        3. useBuiltIns設置爲false比較不錯。
          在js代碼第一行import '@babel/polyfill',或在webpack的入口entry中寫入模塊@babel/polyfill,會將@babel/polyfill整個包所有導入;
          最安全,但打包體積會大一些,通常不選用。
      2. 須要安裝的所有依賴:babel

        yarn add babel-loader@8 @babel/core @babel/preset-env -D
        yarn add @babel/polyfill
      3. .babelrc配置文件

        {
          "presets": [
            [
              "@babel/preset-env",
              {
                "modules": false, // 推薦
                "useBuiltIns": "entry", // 推薦
                "corejs": 2, // 新版本的@babel/polyfill包含了core-js@2和core-js@3版本,因此須要聲明版本,不然webpack運行時會報warning,此處暫時使用core-js@2版本(末尾會附上@core-js@3怎麼用)
              }
            ]
          ],
          "plugins": []
        }
    2. 若是是開發第三方類庫: @babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs2(或者,不作轉碼處理,提醒使用者本身作好兼容處理也能夠)。

      1. 須要安裝的所有依賴:

        yarn add babel-loader@8 @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
        yarn add @babel/runtime-corejs2
      2. .babelrc配置文件

        {
          "presets": [
            [
              "@babel/preset-env",
              {
                "modules": false,
              }
            ]
          ],
          "plugins": [
            [
              "@babel/plugin-transform-runtime",
              {
                "corejs": 2 // 推薦
              }
            ]
          ]
        }

參考文檔

  1. babel polyfill 和 runtime 淺析
  2. Babel + Webpack 配置前端項目
相關文章
相關標籤/搜索