webpack是目前使用比較流行的一個前端模塊打包器,前端的任何資源都被當成一個模塊來處理,如圖片、css文件等等。在基於webpack構建的前端項目中,通常都會配置有關css文件處理的規則,這其中也包括css文件中圖片資源的處理,那麼webpack究竟是怎麼處理它的呢?筆者以前也遇到過相似圖片路勁的問題,爲此還寫過一篇博文webpack生成的css文件background-image url圖片沒法加載。今天就來講說webpack是怎麼處理css文件中的圖片路徑的,首先上一個具體的例子。javascript
最近使用umi搭建前端的一個項目,在使用過程可能遇到一個umi的bug,爲此還提出了一個issue 項目配置css module影響到css-loader對第三方庫css文件中圖片url的處理。順便在簡述下:css
在項目中經過設置cssLoaderOptions.modules
爲true來開啓css modulehtml
項目中引入了第三方庫kindeditor的css文件。前端
import 'kindeditor/themes/default/default.css'
該default.css文件中有經過url
引入圖片資源,而且圖片資源非相對路徑寫法,例如其中一處的寫法:java
.ke-toolbar-icon-url { background-image: url(background.png); }
而後經過npm start開啓本地服務進行預覽時,編譯報錯,以下圖:node
奇怪,明明對應的圖片資源是存在的,webpack編譯時爲啥找不到呢?苦苦尋思了一番沒有找到答案,因而一頭扎進webpack和css-loader源碼的海洋中開啓"尋寶"之旅。webpack
不賣關子了,致使上述的直接緣由:git
css-loader沒有對css文件的
url
方法進行處理(轉化爲相對路徑)github
這樣致使webpack在整合通過loader處理後的default.css模塊時,由於模塊用到require(background.png)
來引用圖片資源,此時就用到Nodejs的模塊加載機制,其具體能夠查看本博客另外一篇文章談談npm依賴管理,也能夠查看nodejs官網module章節。nodejs在解析background.png圖片路徑時,會將其解析爲第三方模塊,這樣會從node_modules
中查找,經過在webpack中打印錯誤日誌,能夠從其中看出一些端倪,以下圖,missing字段表示查找過的路徑均沒有找到對應的資源文件。web
umi內部其實使用css-loader-1
(fork css-loader@1.x而來)來處理css文件的, 致使css-loader
沒有對css文件圖片路徑進行處理的底層緣由:
項目開啓css module後,不應影響到node_modules中css文件的css module的狀況而實際上產生了影響;致使沒有對第三方庫中的css文件中圖片路徑進行處理
若是單純爲了解決上面問題就能夠到此爲止,可是處於好奇,畢竟被坑了幾回,想知道webpack是怎麼處理css文件中的圖片路徑的。例如咱們在項目中這樣寫過css:
.xxa { background: url(background.png) }
或者這樣:
.xxb { background: url(~alias/background.png) }
在項目中,不論咱們用less、stylus仍是sass等css預處理庫編寫css,其最終是經過對應的loader如less-loader將編寫的樣式轉換變換爲css,而後經過css-loader
來處理css中有關路徑的轉換,其做用拿其官網的介紹來講:
The
css-loader
interprets@import
andurl()
likeimport/require()
and will resolve them.
最多見處理css樣式的項目,通常通過如下幾個loader從右到左順序執行,拿less編寫的樣式來講,之內聯loader的展現形式來講明:
!!css-loader!post-loader!less-loader!./xxx/xx.less
固然css-loader處理後還要通過style-loader或者mini-css-extract-plugin提供的loader處理,可是這不在本次談論範圍。
下面經過一幅圖來看看通過webpack解析模塊到css產出這一過程,webpack幫咱們作了什麼。
具體就來簡單分析整個流程,可能分析有不正確的地方,還請你們批評指正
首先從入口文件(entry配置的文件)開始構建,使用NormalModuleFactory來解析並建立模塊
NormalModuleFactory使用enhance-resolve
來解析依賴的模塊絕對地址,若是模塊地址解析錯誤就會如文章開頭的問題拋出錯誤,解析正確則會建立依賴模塊。如上圖中的./index.css,模塊地址解析成功後,webpack爲index.css建立的模塊屬性以下圖:
建立的一個模塊,通常包括模塊的type、context、request、userRequest、rawRequest、resource、dependencies和loaders等模塊相關信息。
模塊建立後,會用其依賴處理的loader來編譯模塊內容,模塊依賴的loader存放在模塊的loader屬性數組中;對於css文件最後是用css-loader
來處理。
css-loader官網說的會對css文件的url/@import
進行處理,可是具體實現細節並無詳細闡述。下面來簡單說說對css-loader的主要功能:
轉換css中的url
和@import
爲 require/import
;
例如url
中的地址(絕對地址除外)會被解析爲相對地址,防止webpack在解析模塊地址時出錯;這其中包括webpack alias別名組成的地址和node_moduels庫中地址。順便說下:
css-loader內部是經過
postcss
生成css的ast並遍歷找出其url方法來完成轉換的。
按comonjs模塊的形式生成css文件模塊內容
css文件最終轉換後的commonjs模塊形式,模塊的後綴仍是.css,其內容以下圖所示:
css-loader還處理css module,也是經過遍歷css的ast來完成轉換
這樣經過css-loader完成了css文件中圖片url路徑的轉換,有助於webpack尋找圖片資源的具體位置。
其實,在css-loader處理完css模塊過程當中,會再次經過NormalModuleFactory來解析並建立其內部的圖片模塊,webpack模塊對應的屬性以下圖:
生成圖片對應的commonjs模塊內容可能爲base64的內容,以下圖:
也可能爲圖片資源產出的引用地址,以下圖:
這主要取決於url-loader在處理圖片資源時是否指定limit
配置項值,該值會跟圖片內容大小進行比較。在limit
值小於圖片內容大小時,則使用file-loader
來實現圖片提出到webpack編譯產出的對應位置下。
上面圖片模塊內容爲圖片產出地址,正是file-loader
處理的結果,其實現如下幾項功能:
圖片內容會抽離到webapck的編譯產出位置。
按照loader的name
配置和webpack的publicPath
配置項生成最終的url。
由於圖片的內容被抽離掉,那麼webpack生成的圖片模塊內容應該爲該圖片的引用地址。這涉及到兩部分
根據file-loader的name配置項生成相對地址部分。
以下file-loader配置項:
{ test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader', options: { name: 'static/[name].[hash:8].[ext]', }, }
而後根據loader-utils
的interpolateName
方法解析對應的url。例如上面css文件的圖片地址轉換結果:
./background.png
會轉換爲: static/background.a9153e95.png
根據webpackConfig.output.publishPath
生成圖片的引用地址。
最終生成的地址爲:
__webpack_public_path__ + "static/background.a9153e95.png";
通過上面步驟的處理,咱們看到產出的最終css文件的效果以下圖:
順便說一下,若是產出的css文件通過mini-css-extract-plugin
提供的loader進行統一抽離,那麼它也可能會影響css文件中圖片的引用路徑,尤爲該loader配置了publicPath
內容,以下面loader的配置:
{ loader: MiniCssExtractPlugin.loader, options: { publicPath: 'public/path/to/' } }
那麼css文件中圖片的路徑最終結果以下圖:
這就是webapck經過各類loader處理css中圖片路勁的過程,經過這一過程咱們可能只是大概對這一過程有一個大概的認知,若是要深刻理解仍是須要花時間研究。