在編寫庫的時候,我們有時候會希望按需載入某些依賴,例如如果**的執行環境不支援某些功能的話,就載入相關的 polyfill 。
webpack 作為當前最流行的打包工具,早已支援動態載入的功能了。
本文將討論一種用 webpack 打包含動態載入的類庫的方法。
注意,本文是寫給類庫作者看的,如果讀者寫的是應用,那就沒有必要往下看了。
示例類庫
這個類庫中有個 loaddeps 方法,會根據執行環境檢測 textdecoder 是否存在,如果不存在就會去載入依賴,這些依賴是在本地目錄中。// my-lib.jsclass mylib else
});}
initialize() }
當我們把類庫發布到 npm 之後,我們就會這麼使用它:
constructor() ,
// ...};然後輸出了這些檔案:
// webpack outputbuilt at: 02/19/2019 5:08:41 pm
asset size chunks chunk names
1.js 79 bytes 1 [emitted]
2.js 79 bytes 2 [emitted]
3.js 79 bytes 3 [emitted]
index.js 144 kib 0 [emitted] main
entrypoint main = index.js
/1.js/2.js/3.js
但是結果呢?當然是 404 了。因為**的根目錄沒有這些檔案呀,除非你把這些檔案複製到**根目錄。
同樣道理,如果 publicpath 是相對路徑的話(假設是 『』),那麼請求依賴的路徑就會相對於當前的 url 了,即如果當前 url 是 /topics/:uid,那請求的路徑就會是 /topics/:uid/1.js。除非當前目錄下有這些依賴,不然結果還是 404。
當然,我們還可以修改服務端配置,把 /1.js 和 **/1.js 都指向 /path/to/project/node_modules/my-lib/dist/1.js。
對應用層來說,為了乙個庫而改伺服器配置就顯得太麻煩了。
對乙個類庫來說,它應該管理好自己的依賴,不應該讓應用層甚至是伺服器端來配合。
解決方案
我們需要解決這個路徑問題,既要保證結果正確,又要方便開發。
很明顯,這個問題是因為 webpack 打包的時候處理了 import() 導致的,如果我們不在編譯時處理,而是執行時處理,這不就可以達到目的了嗎?
先來準備一下 dist/ 的目錄結構, 修改 webpack 的配置:
copywebpackplugin 會把 src/deps/ 中的檔案複製到 dist/ 目錄中,這時候 dist/ 就會變成:const copywebpackplugin = require('copy-webpack-plugin');module.exports = ,
plugins: [
new copywebpackplugin()
]}
方案一:由應用層處理dist
├── 1.js├── 2.js├── 3.js├── text-encoding.js
├── other-dep.js
├── another-dep.js
└── index.js
這個方案很容易實現,只需要讓應用層來呼叫 import() 就可以了。
這種做法非常簡單,而且在開發環境下也不需要任何處理就能正常執行了。// my-lib.jsclass mylib
loaddeps() else
} else
});constructor() );
}}
不足之處是如果有多個專案都引用了這個類庫的話,那麼當類庫新增了新的依賴時,所有引用了這個類庫的專案都要改動原始碼。這會是乙個比較繁瑣的事情。
對於這個解決方案,其實還有乙個變種,相對來說更加方便:
注意:這個變種方案有可能會出現 critical dependency: the request of a dependency is an expression 的報錯。// my-lib.jsclass mylib
loaddeps() else
});constructor() );
}}
方案二:由類庫處理
這個方案稍微複雜一點點。
想要 webpack 不處理 import(),那麼就不能讓 webpack 去解析含有 import() 的檔案,即需要把含有載入依賴的部分分離到另乙個檔案中。
// runtime.jsmodule.exports = }
注意:因為 webpack 不會解析這個檔案,loader 就不會處理這個檔案,所以這個檔案裡面最好使用 node.js 原生支援的語法。
然後修改 webpack 配置:// my-lib.jsimport runtime from './runtime';class mylib else
});}}
這樣,webpack 在處理 my-lib.js 的時候會把 runtime.js 載入進來,但不會去解析它。所以會得到以下的結果:module.exports = ,
module: ,
plugins: [ ... ]
}
如果應用層引用了這個類庫,那麼 webpack 打包應用的時候就會處理類庫中的 import(),這樣就和應用層平時的動態載入一樣了,上面的問題也就解決了。// dist/index.js/******/ ([/* 0 *//***/ (function(module, exports) };/***/ }),/* 1 *//***/ (function(module, __webpack_exports__, __webpack_require__)
var _proto = mylib.prototype;
_proto.loaddeps = function loaddeps() else
});};
_proto.initialize = function initialize() ;
return mylib;}();// ...
最後剩下乙個問題,那就是在開發環境下,我們也需要測試 runtime.js,但這時候它是 import(『my-lib/dist/***』) 的,這個肯定會報 error: cannot find module 的錯誤。
這時候可以像方案一那樣,用 import(importprefix + 『/text-encoding』) 的方式來解決,也可以利用 normalmodulereplacementplugin 來解決。
這個外掛程式可以改變重定向資源,上面的配置是把 my-lib/dist/* 裡面的資源都重定向到 ../src/deps。// webpack.dev.jsmodule.exports = ),
]}
更多詳細的用法,可以參考下官方文件 normalmodulereplacementplugin。
注意:這個外掛程式最好只用在開發環境中。
這個方案雖然有些繁瑣,但最大的優點是可以把依賴管理都交給類庫自己處理,不需要應用層干預。就算類庫日後改變打包位置(不再是 dist/) 也無需讓應用層知道。
軟工文件你知道嗎?
前言 從軟體開發周期來看 從文件型別來看 通過以上這兩個導圖,我們大致可以分清在什麼階段需要寫什麼文件,這對於我們以後的工作是很有幫助的。需要注意的是我們是根據文件來開發軟體,而不是開發完軟體之後根據你所開發的詳情寫文件。那我們編寫文件的作用是什麼呢?文件名稱 文件的作用 可行性研究報告 從經濟 技...
Apple 如何知道你使用了私有API
寫在開頭 ios開發者 群532084214 給大家提供乙個交流技術 也可以聊天打屁的平台 otool l 這個工具可以清晰的列出你鏈結所有的庫 像io.kit是不允許使用的 nm u 這個工具可以清晰的列出你所有鏈結符號 如 c方法 oc方法 檢查所有selecter的字串 occlass sel...
如何使用webpack打包JS
我們在命令列中輸入 webpack h看看webpack的命令列大全 usage webpack cli options webpack cli options entry output webpack cli options outputwebpack告訴我們,用webpack進行打包有三種方法 ...