【深入理解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 模組系統。