Webpack構建library時的踩坑經歷

背景

隨著組件化、模塊化意識地不斷增強,越來越多同學開始構建npm library來供業務復用了。今天收到一位同學的反饋,問題是構建出的package在使用時,會拋出ReactDOM找不到的異常,後來通過查看構建出的源碼,結合webpack打包原理,找到了解決方案,在此分享給大家。

案發現場

首先,我們回顧一下案發現場,在項目中興緻勃勃地引入了構建好的npm包後,但是一運行就會報如下錯誤:

Cant resolve ReactDOM in xxxx n

我們期望從構建好的文件尋找些蛛絲馬跡,源碼頭部如下所示:

(function webpackUniversalModuleDefinition(root, factory) {n if(typeof exports === object && typeof module === object)n module.exports = factory(require("React"));n else if(typeof define === function && define.amd)n define("module", ["React"], factory);n else if(typeof exports === object)n exports["module"] = factory(require("React"));n elsen root["module"] = factory(root["React"]);n})n

我們能夠看到,頭部的腳本已經是umd格式化後的了,表明其可以在BrowserAMDCommonJS環境下均可以使用,並且觀察到ReactreactDOM依賴是直接引用進來的,說明打包時的externals的確是設置過的,而且設置很有可能如下:

{n react:React,n react-dom:ReactDOMn}n

打包格式和資源抽離已經做好,為什麼還會出錯呢?仔細觀察錯誤,webpack報錯ReactDOM依賴找不到,我們嘗試將其構建的代碼遷移到項目里,編寫如下:

const ReactDOM = require(ReactDOM);n

與真實業務代碼相比較,我們通常require的不是ReactDOM,而是react-dom,只是在構建的時候,webpack會根據externals里的配置,將依賴進行替換。

解決方案

恍然大悟,我們意識到在CommonJS下引入的依賴名應該為react-dom,而非瀏覽器環境下注入在全局變數里的ReactDOM故只需要想方設法將打包好的文件里對CommonJS環境下的依賴進行修改為react-dom即可。

深入理解externals

聰明的webpack早已經幫我們想好了這點,接下來我們通過深入externals參數來解決此問題。

我們通常情況下配置的externals參數為字元串格式,其表達的意思為在全局變數里查找此變數,例如如下配置代表使用window.jQuery

{n jquery:jQueryn}n

為了支持不同環境下,使用不同的依賴,externals提供了object格式的配置,例如如下配置表名了在全局變數環境下使用ReactDOM變數,在其他環境下使用react-dom對象

{n react-dom : {n commonjs: "react-dom",n amd: "react-dom",n root: "ReactDOM"n }n}n

結合以上知識點,我們修改webpack的配置參數並且重新構建,得到的代碼如下所示,可以看到,react等資源的依賴名已經被校正過來,僅僅在瀏覽器環境下才去使用React變數,問題也就迎刃而解了。

(function webpackUniversalModuleDefinition(root, factory) {n if(typeof exports === object && typeof module === object)n module.exports = factory(require("react"));n else if(typeof define === function && define.amd)n define("module", ["react"], factory);n else if(typeof exports === object)n exports["module"] = factory(require("react"));n elsen root["module"] = factory(root["React"]);n})n

構建library的小建議

文末,我們再來總結一下有關webpack打包library的要點。

  • 明確要運行的環境,既可以通過npm包安裝,也可以提供cdn資源供他人使用,那麼需要使用umd格式進行輸出。
  • 為了支持umd格式,我們需要設置webpack的libraryTarget參數為umd,它決定了構建內容的運行環境。
  • externals本意為排除第三方資源,如果我們使用了umd格式輸出,那麼額外需要我們精準地控制每一種環境下的資源引入方式,因此webpack也提供了對象格式的配置來支持這些場景。

libraryTarget及externals的正確使用,是構建library的基本要求,理解了它們的背後原理後,是時候構建你自己的library了。

參考資料

  • Webpack 外部擴展(Externals)
  • dawn里的webpack配置參考

推薦閱讀:

2017年零基礎轉行前端還能找到工作么?
你會用CSS實現垂直居中嗎?
2016 這一年啊~
前端學習-Markdown語法小結

TAG:webpack | 前端开发 |