以前一篇文章分析了Webpack打包JS模塊的基本原理,所介紹的案例是最多見的一種狀況,即多個JS模塊和一個入口模塊,打包成一個bundle文件,能夠直接被瀏覽器或者其它JavaScript引擎執行,至關於直接編譯生成一個完整的可執行的文件。不過還有一種很常見的狀況,就是咱們要構建發佈一個JavaScript的庫,好比你在npm社區發佈本身的庫,這時Webpack就須要相應的配置,編譯生成的代碼也會略有不一樣。javascript
和以前一篇文章同樣,本文主要分析的是Webpack的生成代碼,並結合它來講明編譯庫時Webpack那些關於library的配置選項的具體做用,相應的官方文檔在這裏。vue
咱們仍是從簡單的案例開始,咱們隨便編寫一個簡單的庫util.js
:java
import $ from 'jquery' function sayHello() { console.log("Hello"); } function hideImages() { $('img').hide(); } export default { sayHello: sayHello, hideImages: hideImages }
提供了兩個函數,固然它們之間毫無關係,也實際上沒有任何卵用,純粹是僅供教學參考。。。jquery
接下來寫Webpack的配置:webpack
// 入口文件 entry: { util: './util.js', } // 輸出文件 output: { path: './dist', filename: '[name].dist.js' }
但僅僅這樣是不夠的,這樣輸出的文件就是一個當即執行的函數,最後會返回util.js
的exports,參照上一篇文章中分析,最後生成的bundle代碼結構大體是這樣的:web
(function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js'); }) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery });
它若是執行完,那就結束了,將util.js
的export部分返回而已,而咱們須要的是將這個返回值交給編譯後的文件的module.export
,這樣編譯後的文件就成了一個能夠被別人import
的庫。因此咱們但願獲得的編譯文件應該是這樣的:npm
module.exports = (function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js'); }) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery });
要獲得這樣的結果,Webpack配置output部分須要加入library信息:segmentfault
// 入口文件 output: { path: './dist', filename: '[name].dist.js', library: 'util', libraryTarget: commonjs2 }
這裏最重要的就是libraryTarget
,咱們如今採用commonjs2
的格式,就會獲得上面的編譯結果,也就是說Webpack會library把最後的輸出以CommonJS的形式export出來,這樣就實現了一個庫的發佈。瀏覽器
除了commonjs2,libraryTarget
還有其它選項:ide
var (默認值,發佈爲全局變量) commonjs commonjs2 amd umd
使用不一樣的選項,編譯出來的文件就可以在不一樣JavaScript執行環境中使用。在這裏咱們直接看萬金油umd
格式的輸出是怎麼樣的:
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') // commonjs2 module.exports = factory(); else if(typeof define === 'function' && define.amd) define("util", [], factory); // amd else if(typeof exports === 'object') exports["util"] = factory(); // commonjs else root["util"] = factory(); // var }) (window, function() { return (function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js'); }) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery }); }
比以前的commonjs2的狀況要複雜得多,由於它須要處理各類不一樣的case,但其實後面的部分都是同樣的,最重要的就是最前面的幾行,這是umd模塊的標準寫法。它運行傳入的factory
函數,實際上就是加載模塊的函數,而後把返回的結果根據不一樣的運行環境交給相應的對象。例如var
,那就會把結果設置爲一個全局變量,這用於瀏覽器經過<script>
標籤直接導入該JS文件;若是是CommonJS
,它則交給exports
對象;若是是AMD
環境,它也是用標準的AMD的寫法。這樣這個發佈出來的JS庫就能夠在任意的環境中都能被其餘人使用。
targetExport
控制輸出內容若是用umd
格式打包,可能會有一個坑須要注意,若是你的庫的源代碼是用ES6格式export default
來輸出的,正如上面的例子util.js
,你直接把編譯後的JS庫文件放到瀏覽器中使用,能夠是<script>
,或者RequireJS
,可能得不到你想要的結果。這是由於你的JS文件返回給你的對象是這樣的:
{ 'default': { sayHello: sayHello, hideImages: hideImages } }
而不是你所指望的:
{ sayHello: sayHello, hideImages: hideImages }
不只是瀏覽器,在不支持ES6的模塊系統中一樣會出這個問題,就是由於它們並不認識default
。因此你編譯後的JS文件實際上應當只輸出default,這就須要在Webpack配置裏用targetExport
來控制:
library: 'util', libraryTarget: umd, targetExport: 'default'
這樣上面的模塊加載函數factory
會在返回值後面加一個['default']
,這樣就只返回exports
的default部分。
這個坑在umd格式下其實仍是挺容易踩到的,例如你發佈一個Vue
組件,.vue
文件中的JavaScript部分通常都是把Component對象以export default
的格式導出的,就像這樣:
export default { name: 'xxx', data() { return // ... }, props: { // ... } methods: { // ... } }
若是你把編譯後的JS文件直接放在瀏覽器裏運行,而且用CDN的方式加載Vue
,你會發現Vue沒法識別這個Component,由於你獲得的這個對象多了一層沒必要要的default
。
你可能會問若是我把輸出內容改爲了default
,會不會影響這個模塊在ES6環境下的使用?通常來講是不會的。以前一篇文章裏已經談到,Webpack的生成代碼在引入一個模塊時,會經過一個叫__esModule
的值來設置和判斷它是否是ES6格式的export,如今若是隻導出default部分,那麼這個對象是被視爲非ES6的,由於它不含__esModule
。這樣其它模塊經過import
來引入這個模塊時,會引入整個對象,這實際上變相地就等價於只引入原模塊的export default
部分。
固然以上討論的前提是,你全部須要export的內容所有都在export default
裏。若是你既有default,又有正常的export,那編譯後的文件只導出default部分顯然是不行的。