webpack 很強大,提供的能力選項配置也很多好讓你滿足各種不同的打包場景。
但學習成本也跟着上去了,其中一件讓人頭疼的是輸出時的配置,特別容易讓人迷惑;
平時用 webpack 可能不會有太大問題,可一旦你開發的包被別人引用的時候,就會存在問題;我最近遇到這麼一個場景耗費我很多時間去重新學習 webpack 的打包輸出。
本文總結了自己開發庫時依賴共同第三方包時的 externals 和 output 配置項。
1、場景
當你開發的包依賴較大的第三方包(react
、react-dom
) 的時候,你一般是把這些大的第三方包 externals 出去:
1{
2 externals: {
3 'react': 'React',
4 'react-dom': 'ReactDOM'
5 }
6}
假如你開發了 A、B 兩個插件
A 依賴 B 、
react
和react-dom
B 只依賴
react
和react-dom
如果你想發佈 A 的話,有兩種策略,要麼直接依賴(將 B 寫到 dependencies
中),要麼像 react
和 react-dom
一樣 externals 掉 B 包(有可能 B 的包也很大)。
請問此時該如何 webpack 配置來支持常用的兩種模式?
2、如何解決?
先看一下 webpack 官方在進行打包時候,是根據 package.json 中的 mainFields
字段指定依據哪個字段中的 路徑 找到第三方包的,而 externals
字段則是指定以何種方式引入第三方包
客觀情況如下:
我們通常的是會把打出來的包放在
browser
字段中;webpack 打包時,選擇依賴包是根據 resolve.mainFields 字段找到指定的路徑把代碼打進去的;默認配置是
['browser', 'module', 'main']
,也就是說會優先找browser
的字段指定的路徑libraryTarget
配置如何暴露library
。如果不設置library
, 那這個library就不暴露。就相當於一個自執行函數libraryTarget
決定了你的library
運行在哪個環境,哪個環境也就決定了你哪種模式去加載所引入的額外的包。也就是說,externals
應該和libraryTarget
保持一致。library
運行在瀏覽器中的,你設置externals
的模式爲commonjs
,那代碼肯定就運行不了了。
我們一般容易混淆的是 externals
的使用,比如對 react-dom
的 externals,經常會看到兩種寫法:
{'react-dom': 'reactDom'}
{'react-dom': 'react-dom'}
這兩種的區別,其實是和你想將第三方以怎樣的方式打入到你最終代碼有關
還有一種 對象形式,那種是隻應用在 umd 的打包方式中
2.1、externals 資料
externals:官方文檔
webpack externals 深入理解:偏向總結
深入淺出webpack之externals的使用:以打包之後的源碼拆解開來講解
2.2、mainFields 參考資料
先通讀一下官方文檔中的 resolve.mainFields,想看中文的可以看這個鏈接 解析(resolve)
附其他參考資料:
深入淺出webpack學習(5)--Resolve:詳細解讀 webpack 中的這個
resolve
字段用法,相比官方文檔多了舉例package-browser-field-spec:package.json 中
browser
字段的標準寫法。深入理解webpack如何解析代碼路徑:掘金上的文章,講解
webpack
的代碼路徑解析規則
順帶收集幾個相關 issue,看看別人遇到的問題現在你是否可以解決:
`browser` vs `module` fields in `package.json`:回答了當 package.json 同時包含
browser
,module
&main fields
字段時候,如何指定我們 webpack 不用默認的browser
字段
3、解決方案
使用兩份輸出配置項,主要更改 webpack 的打包的配置項中的 externals
、output
這兩個字段。
3.1、B 包的配置如下
第一份配置是針對 .umd.js
文件的(別人用於 externals ,然後通過 script 腳本標籤)
1{
2 externals: {
3 'react': 'React',
4 'react-dom': 'ReactDOM'
5 },
6 output: {
7 filename: 'index.umd.js',
8 libraryTarget: 'umd',
9 library: 'extB',
10 path: path.resolve(__dirname, 'dist'),
11 umdNamedDefine: true
12 }
13}
第二份配置是針對 .browser.js
文件的(別人使用的時候直接放在 dependencies
中,也會最終打包進去)
1{
2 externals: {
3 'react': 'react', // 這裏更改了
4 'react-dom': 'react-dom' // 這裏更改了
5 },
6 output: {
7 filename: 'index.browser.js', // 這裏更改了
8 libraryTarget: 'umd',
9 library: 'extB',
10 path: path.resolve(__dirname, 'dist'),
11 umdNamedDefine: true
12 }
13}
同時還得更改當前文件夾的 package.json 的對應字段如下:
1{
2 ...
3 "main": "dist/index.umd.js",
4 "module": "dist/index.umd.js",
5 "browser": "dist/index.browser.js",
6 ...
7}
3.2、A 包的配置
首先,無論是否 externals,都需要在 package.json
中填完對 B 依賴的信息(可以根據實際情況放在 dependencies
字段或者 peerDependencies
字段)
情況 1 :A 包最終要把 B 包打入到最終代碼中去,那麼和 B 包的 webpack 配置是一樣的;
情況 2:A 包最終要把 B 包 externals 掉,基本配置是一樣的,只不過有額外的兩部需要操作:
在上述的
externals
中新增 B 包的 externals 配置項(需要區別 {'B': 'B'} 和 {'B': 'extB'})在頁面中引入 cdn 資源
http://xxx/index.umd.js
(注意不是http://xxx/index.browser.js
)
4、簡化的寫法
我們看到上述這麼寫是能成功的,官方考慮到了這種情況,所以針對 umd
的打包方式,推出 對象形式,讓你統一上述兩種配置文件(但這種配置只能應用在 umd 的打包方式中);
最終我們把上述兩份打包配置合併成一份:
1{
2 externals: {
3 'react': 'react'
4 'react-dom': {
5 commonjs: 'react-dom', // 這裏更改了
6 commonjs2: 'react-dom', // 這裏更改了
7 amd: 'react-dom', // 這裏更改了
8 root: 'reactDom'
9 }
10 },
11 output: {
12 filename: 'index.umd.js', // 這裏更改了
13 libraryTarget: 'umd',
14 library: 'extB',
15 path: path.resolve(__dirname, 'dist'),
16 umdNamedDefine: true
17 }
18}
這樣就省去了 index.browser.js
這個文件,增加了兼容性。
5、總結
這篇文章是我一整天掉在 webpack 坑裏,來回調試了半天所得出的經驗總結,特此總結形成文章,方便以後查找。
閱讀原文獲取更多的信息
本文分享自微信公衆號 - JSCON簡時空(iJSCON)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。