babel7相關

在前端開發領域,瀏覽器兼容性問題來來去去,不曾消失過。除了 CSS,我們還要面對 JavaScript 的兼容性問題:你用了 JavaScript 的 A 特性,能夠在 B 瀏覽器上正常運行,卻在 C 瀏覽器的 D 版本上報錯。

@babel/cli是 babel 提供的命令行工具,用於命令行下編譯源代碼。

這裏假定我們已通過 npm init 初始化項目。
首先,在項目中安裝@babel/cli:

npm install --save-dev @babel/core @babel/cli

如果你用過 babel 6,可能要問,怎麼不是 npm install --save-dev babel-cli?@ 符號又是什麼?這是 babel 7 的一大調整,原來的 babel-xx 包統一遷移到babel 域下 - 域由 @ 符號來標識,一來便於區別官方與非官方的包,二來避免可能的包命名衝突。

現在假定我們的項目下有一個 script.js 文件,內容是:

let fun = () => console.log('hello babel.js')

我們試試運行 npx babel script.js:

$ npx babel script.js
let fun = () => console.log('hello babel.js');

這個調整則是在 babel 6 裏發生的。Babel 6 做了大量模塊化的工作,將原來集成一體的各種編譯功能分離出去,獨立成插件。這意味着,默認情況下,當下版本的 babel 不會編譯代碼。

現有如下僞代碼:

class classApi{
  //格式化時間 
  static formdatatime = (time)=>{
    let date = new Date(time * 1000)
    const getDoubleDigit = curr => curr < 10 ? `0${curr}` : curr;
    return `${date.getFullYear()}-${getDoubleDigit(date.getMonth() + 1)}-${getDoubleDigit(date.getDate())} ${getDoubleDigit(date.getHours())}:${getDoubleDigit(date.getMinutes())}:${getDoubleDigit(date.getSeconds())}`
  } 
  // 構造函數
  constructor(localItem){
      this.initData(localItem);
      this.initParams();
  }
  // 獲取參數的具體規則
  generateParams(params){
   
  }
  // 獲取參數的代理方法
  ProxyParams(key){
    
  }
  //主函數 用於生成具體的api
  main(key){
    
  }
  // 初始化數據
  initData(localItem){
    
  }
  //初始化參數 apiObject對象是需要維護的對象
  initParams(){
    
  }
  // 生成api的工廠
  generateApi(){
    
  }
}

const classApiObject = new classApi("roomData");
classApiObject.generateApi();

使用如下命令編譯

npx babel --no-babelrc dist/classApi.js --out-dir dist --presets=@babel/preset-env --plugins=@babel/plugin-proposal-class-properties

–no-babelrc : 表示不使用babelrc
–out-dir : 指定編譯輸出目錄
–presets : 指定babel的預設
–plugins: 指定babel 插件

則生層如下代碼

"use strict";

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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

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

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

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 classApi =
/*#__PURE__*/
function () {
  //格式化時間 
  // 構造函數
  function classApi(localItem) {
    _classCallCheck(this, classApi);

    this.initData(localItem);
    this.initParams();
  } // 獲取參數的具體規則


  _createClass(classApi, [{
    key: "generateParams",
    value: function generateParams(params) {
 
    } // 獲取參數的代理方法

  }, {
    key: "ProxyParams",
    value: function ProxyParams(key) {
      
    } //主函數 用於生成具體的api

  }, {
    key: "main",
    value: function main(key) {
     
    } // 初始化數據

  }, {
    key: "initData",
    value: function initData(localItem) {
     
    } //初始化參數  apiObject對象是需要維護的對象

  }, {
    key: "initParams",
    value: function initParams() {
      
    } // 生成api的工廠

  }, {
    key: "generateApi",
    value: function generateApi() {
      
    }
  }]);

  return classApi;
}();

_defineProperty(classApi, "formdatatime", function (time) {
  var date = new Date(time * 1000);

  var getDoubleDigit = function getDoubleDigit(curr) {
    return curr < 10 ? "0".concat(curr) : curr;
  };

  return "".concat(date.getFullYear(), "-").concat(getDoubleDigit(date.getMonth() + 1), "-").concat(getDoubleDigit(date.getDate()), " ").concat(getDoubleDigit(date.getHours()), ":").concat(getDoubleDigit(date.getMinutes()), ":").concat(getDoubleDigit(date.getSeconds()));
});

var classApiObject = new classApi("roomData");
classApiObject.generateApi();

babel-polyfill

babel-polyfill 其實包含regenerator runtime、core-js,如果你的代碼只需要其中一部分 polyfill,那麼你可以考慮直接引入 core-js 下的特定 polyfill,不必使用 babel-polyfill 這樣的龐然大物。

另一種辦法,是配合 @babel/preset-env 的 useBuiltIns 配置
當然你還可以引入cdn上的包

babel-runtime

@babel/runtime 是 babel 生態裏最讓人困惑的一個包。而在 babel 7 下,我們還多了一個 @babel/runtime-corejs2。
我們先來看看 @babel/runtime 的 package.json 裏的 description 怎麼寫:

babel’s modular runtime helpers

有點不知所謂。

不過從 package.json 裏沒有 main 字段我們可以看出,它的用法肯定不是 require(‘babel-runtime’) 這樣。

那麼,babel-runtime 與 babel-polyfill 的區別究竟是什麼?

我們知道,IE 11 不支持 Object.assign,此時,我們有倆種候選方案:

  1. 引入 babel-polyfill,補丁一打,Object.assign 就被創造出來
  2. 配置 @babel/plugin-transform-object-assign

第二種方案中,babel 會將所有的 Object.assign 替換成 _extends 這樣一個輔助函數。如下所示:

Object.assign({}, {})

編譯成:

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

_extends({}, {});

問題是,如果你的項目裏有 100 個文件,其中有 50 個文件裏寫了 Object.assign,那麼,壞消息來了,_extends 輔助函數會出現 50 次。

怎麼辦?我們自然而然會想到把 _extends 分離出去,然後在每個文件中引入 - 這正是 @babel/runtime 的作用:
非常漂亮。可沒人想要手動轉換這些代碼。

於是 babel 提供了 @babel/plugin-transform-runtime 來替我們做這些轉換。

這樣,我們不需要 babel-polyfill 也一樣可以在程序中使用 Object.assign,編譯後的代碼最終能夠正常運行在 IE 11 下。

提問:在經過 @babel/plugin-transform-runtime 的處理後,IE 11 下現在有 Object.assign 嗎?

答案是,仍然沒有。

這正是 babel-polyfill 與 babel-runtime 的一大區別,前者改造目標瀏覽器,讓你的瀏覽器擁有本來不支持的特性;後者改造你的代碼,讓你的代碼能在所有目標瀏覽器上運行,但不改造瀏覽器。

如果你還是困惑,我推薦一個非常簡單的區分方法 - 打開瀏覽器開發者工具,在 console 裏執行代碼:

引入 babel-polyfill 後的 IE 11,你可以在 console 下執行 Object.assign({}, {})
而引入 babel-runtime 後的 IE 11,仍然提示你:Object doesn't support property or method 'assign'

babel-register

經過 babel 的編譯後,我們的源代碼與運行在生產下的代碼是不一樣的。

babel-register 則提供了動態編譯。換句話說,我們的源代碼能夠真正運行在生產環境下,不需要 babel 編譯這一環節。

我們先在項目下安裝 babel-register:

$ npm install --save-dev @babel/register

然後在入口文件中 require

require('@babel/register')
require('./app')

在入口文件頭部引入 @babel/register 後,我們的 app 文件中即可使用任意 es2015 的特性。
當然,壞處是動態編譯,導致程序在速度、性能上有所損耗

babel-node

我們上面說,babel-register 提供動態編譯,能夠讓我們的源代碼真正運行在生產環境下 - 但其實不然,我們仍需要做部分調整,比如新增一個入口文件,並在該文件中 require(’@babel/register’)。而 babel-node 能真正做到一行源代碼都不需要調整:

$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.js

只是,請不要在生產環境中使用 babel-node,因爲它是動態編譯源代碼,應用啓動速度非常慢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章