1. 程式人生 > >【深入理解webpack】library,libraryTarget,externals的區別及作用

【深入理解webpack】library,libraryTarget,externals的區別及作用

經過前端框架的迅猛發展,大家開始慢慢對模組化習以為常但卻不知道通過script引入和require(import)引入的區別。如何引入取決於之前如何匯出,在重構一個驗證碼老專案時,不知如何把專案匯出為可通過script引入後使用內部方法?

情景簡化為如下:

  • 外掛程式碼
import {util} from 'util'
import styles from 'css'
export function initA(){
    console.log('it is init')
    ...
}
  • 通過常見webpack打包為bundle.js,在html中引入
<script
src="bundle.js">
</script> <script> //在這裡想要呼叫內部initA方法,報錯initA undefined initA() </script>

分析原因

頁面報錯initA undefined,顯然此時呼叫initA,代表在window物件上面尋找initA方法,因為模組化開發,杜絕一切全域性變數,所以在全域性找不到該物件,它是區域性變數,打包之後程式碼簡化如下:

(function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule"
, { value: true }); exports.initA = initA; function initA() { console.log('it is initA'); } })

那如何可以匯出變數並掛載在全域性物件之下,看下常見的jquery是如何操作的

//jQuery 1.2.6(新版已經支援模組化)
var _jQuery = window.jQuery,
        _$ = window.$;
    var jQuery = window.jQuery = window.$ = function( selector, context ) {
        //
The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context ); };

經過多番尋找在webpack配置中用output.libraryTarget引數可以配置輸出模組規範。

webpack libraryTarget 引數說明

官網說明中敘述libraryTarget 有如下引數可選:(不指定 output.library 將取消這個 “var” 配置)中文說明

  • libraryTarget: “var”(default)
    output.library 會將值作為變數宣告匯出(當使用 script 標籤時,其執行後在全域性作用域可用)。
  • libraryTarget: “window”
    當 library 載入完成,入口起點的返回值將分配給 window 物件。
window["MyLibrary"] = _entry_return_;
// 使用者將會這樣呼叫你的 library:
window.MyLibrary.doSomething();
  • libraryTarget: “assign”
  • libraryTarget: “this”
  • libraryTarget: “global”
  • libraryTarget: “commonjs”
    當 library 載入完成,入口起點的返回值將分配給 exports 物件。這個名稱也意味著模組用於 CommonJS 環境
exports["MyLibrary"] = _entry_return_;
// 使用者將會這樣呼叫你的 library:
require("MyLibrary").doSomething();
  • libraryTarget: “commonjs2”
  • libraryTarget: “amd”
  • libraryTarget: “umd”
    這是一種可以將你的 library 能夠在所有的模組定義下都可執行的方式(並且匯出的完全不是模組)。它將在 CommonJS, AMD 環境下執行,或將模組匯出到 global 下的變數
    最終輸出:
(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    define([], factory);
  else if(typeof exports === 'object')
    exports["MyLibrary"] = factory();
  else
    root["MyLibrary"] = factory();
})(this, function() {
  //這個模組會返回你的入口 chunk 所返回的
});
  • libraryTarget: “jsonp”

對比var,window,global,umd區別

由於瀏覽器環境和node環境的區別,所以產生了window(客服端瀏覽器)和global(node服務端)的區別。
我理解的var即在script匯入時和window一致,是否可以通過import匯入,匯入之後的使用還待解釋。
umd即支援所有情況的自定義。
總的說設定library即在當前環境的全域性引入庫檔案。

externals的簡單使用

那externals又是如何使用的?和模組匯出有什麼區別?
先看定義:externals 配置選項提供了「從輸出的 bundle 中排除依賴」的方法。也就是說webpack打包時不會把庫打入bundle中,所以需要開發者在html中通過script標籤引入。
例如,從 CDN 引入 jQuery,而不是把它打包:

index.html

<script src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"></script>

webpack.config.js

externals: {
  jquery: 'jQuery'
}

有心的同學可能要想了,我都從script標籤引入了那麼全域性就都可以使用了,為什麼還要設定這個配置吶?
為了不改動原來的依賴模組!如下

import $ from 'jquery';

$('.my-element').animate(...);

具有外部依賴(external dependency)的 bundle 可以在各種模組上下文(module context)中使用,例如 CommonJS, AMD, 全域性變數和 ES2015 模組。這裡所說的模式就是上文libraryTarget的模式。
外部 library 可能是以下任何一種形式:

  • root - 外部 library 能夠作為全域性變數使用。使用者可以通過在 script 標籤中引入來實現。這是 externals 的預設設定。
  • commonjs - 使用者(consumer)應用程式可能使用 CommonJS 模組系統,因此外部 library 應該使用 CommonJS 模組系統,並且應該是一個 CommonJS 模組。
  • commonjs2 - 類似上面幾行,但匯出的是 module.exports.default。
  • amd - 類似上面幾行,但使用 AMD 模組系統。