加載圖片
使用file-loader
file-loader可以把JavaScript和CSS中導(dǎo)入圖片的語句替換成正確的地址,并同時把文件輸出到對應(yīng)的位置。例如CSS源碼是這樣寫的:
#app {
background-image: url(./imgs/a.png);
}
被file-loader轉(zhuǎn)換后輸出的CSS會變成這樣:
#app {
background-image: url(5556e1251a78c5afda9ee7dd06ad109b.png);
}
并且在輸出目錄dist中也多出./imgs/a.png對應(yīng)的圖片文件5556e1251a78c5afda9ee7dd06ad109b.png,輸出的文件名是根據(jù)文件內(nèi)容的計算出的Hash值。
同理在JavaScript中導(dǎo)入圖片的源碼如下:
import imgB from './imgs/b.png';
window.document.getElementById('app').innerHTML = `
<img src="${imgB}"/>
`;
經(jīng)過file-loader處理后輸出的JavaScript代碼如下:
module.exports = __webpack_require__.p + "0bcc1f8d385f78e1271ebfca50668429.png";
也就是說imgB的值就是圖片對應(yīng)的URL地址。
在Webpack中使用file-loader非常簡單,相關(guān)配置如下:
module.exports = {
module: {
rules: [
{
test: /\.png$/,
use: ['file-loader']
}
]
}
};
使用 url-loader
url-loader 可以把文件的內(nèi)容經(jīng)過base64編碼后注入到JavaScript或者CSS中去。
例如CSS源碼是這樣寫的:
#app {
background-image: url(./imgs/a.png);
}
被url-loader轉(zhuǎn)換后輸出的CSS會變成這樣:
#app {
background-image: url(data:image/png;base64,iVBORw01afer...); /* 結(jié)尾省略了剩下的 base64 編碼后的數(shù)據(jù) */
}
同理在JavaScript中效果也類似。
從上面的例子中可以看出url-loader會把根據(jù)圖片內(nèi)容計算出的 base64 編碼的字符串直接注入到代碼中,由于一般的圖片數(shù)據(jù)量巨大, 這會導(dǎo)致JavaScript、CSS文件也跟著變大。 所以在使用url-loader時一定要注意圖片體積不能太大,不然會導(dǎo)致JavaScript、CSS文件過大而帶來的網(wǎng)頁加載緩慢問題。
一般利用url-loader把網(wǎng)頁需要用到的小圖片資源注入到代碼中去,以減少加載次數(shù)。因為在HTTP/1協(xié)議中,每加載一個資源都需要建立一次HTTP鏈接, 為了一個很小的圖片而新建一次HTTP連接是不劃算的。
url-loader考慮到了以上問題,并提供了一個方便的選擇limit,該選項用于控制當文件大小小于limit時才使用url-loader,否則使用fallback選項中配置的loader。 相關(guān)Webpack配置如下:
module.exports = {
module: {
rules: [
{
test: /\.png$/,
use: [{
loader: 'url-loader',
options: {
// 30KB 以下的文件采用 url-loader
limit: 1024 * 30,
// 否則采用 file-loader,默認值就是 file-loader
fallback: 'file-loader',
}
}]
}
]
},
};
除此之外,你還可以做以下優(yōu)化:
- 通過 imagemin-webpack-plugin 壓縮圖片;
- 通過 webpack-spritesmith 插件制作雪碧圖。
以上加載圖片的方法同樣適用于其它二進制類型的資源,例如PDF、SWF等等。
加載 SVG
SVG 作為矢量圖的一種標準格式,已經(jīng)得到了各大瀏覽器的支持,它也成為了Web中矢量圖的代名詞。 在網(wǎng)頁中采用SVG代替位圖有如下好處:
- SVG相對于位圖更清晰,在任意縮放的情況下后不會破壞圖形的清晰度,SVG能方便地解決高分辨率屏幕下圖像顯示不清楚的問題。
- 在圖形線條比較簡單的情況下,SVG文件的大小要小于位圖,在扁平化UI流行的今天,多數(shù)情況下SVG會更小。
- 圖形相同的SVG比對應(yīng)的高清圖有更好的渲染性能。
- SVG采用和HTML一致的XML語法描述,靈活性很高。
畫圖工具能導(dǎo)出一個個.svg文件,SVG的導(dǎo)入方法和圖片類似,既可以像下面這樣在CSS中直接使用:
body {
background-image: url(./svgs/activity.svg);
}
也可以在HTML中使用:
<img src="./svgs/activity.svg"/>
也就是說可以直接把SVG文件當成一張圖片來使用,方法和使用圖片時完全一樣。 所以使用file-loader和使用url-loader對SVG來說同樣有效,只需要把Loader test配置中的文件后綴改成.svg,代碼如下:
module.exports = {
module: {
rules: [
{
test: /\.svg/,
use: ['file-loader']
}
]
},
};
由于SVG是文本格式的文件,除了以上兩種方法外還有其它方法,下面來一一說明。
使用 raw-loader
raw-loader 可以把文本文件的內(nèi)容讀取出來,注入到JavaScript或CSS中去。
例如在JavaScript中這樣寫:
import svgContent from './svgs/alert.svg';
經(jīng)過raw-loader處理后輸出的代碼如下:
module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\"... </svg>" // 末尾省略 SVG 內(nèi)容
也就是說 svgContent的內(nèi)容就等于字符串形式的SVG,由于SVG 本身就是HTML元素,在獲取到SVG內(nèi)容后,可以直接通過以下代碼將SVG插入到網(wǎng)頁中:
window.document.getElementById('app').innerHTML = svgContent;
使用raw-loader時相關(guān)的Webpack配置如下:
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: ['raw-loader']
}
]
}
};
由于raw-loader會直接返回SVG的文本內(nèi)容,并且無法通過CSS去展示SVG的文本內(nèi)容,因此采用本方法后無法在CSS中導(dǎo)入SVG。 也就是說在 CSS 中不可以出現(xiàn)background-image: url(./svgs/activity.svg)這樣的代碼,因為background-image: url(<svg>...</svg>)是不合法的。
使用 svg-inline-loader
svg-inline-loader 和上面提到的raw-loader非常相似, 不同在于svg-inline-loader會分析SVG的內(nèi)容,去除其中不必要的部分代碼,以減少SVG的文件大小。
在使用畫圖工具如Adobe Illustrator、Sketch制作SVG后,在導(dǎo)出時這些工具會生成對網(wǎng)頁運行來說不必要的代碼。 舉個例子,以下是Sketch導(dǎo)出的SVG的代碼:
<svg class="icon" verison="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
stroke="#000">
<circle cx="12" cy="12" r="10"/>
</svg>
被svg-inline-loader處理后會精簡成如下:
<svg viewBox="0 0 24 24" stroke="#000"><circle cx="12" cy="12" r="10"/></svg>
也就是說svg-inline-loader增加了對SVG的壓縮功能。
使用svg-inline-loader時相關(guān)的Webpack配置如下:
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: ['svg-inline-loader']
}
]
}
};
加載 Source Map
由于在開發(fā)過程中經(jīng)常會使用新語言去開發(fā)項目,最后會把源碼轉(zhuǎn)換成能在瀏覽器中直接運行的 JavaScript 代碼。 這樣做雖能提升開發(fā)效率,在調(diào)試代碼的過程中你會發(fā)現(xiàn)生成的代碼可讀性非常差,這給代碼調(diào)試帶來了不便。
Webpack支持為轉(zhuǎn)換生成的代碼輸出對應(yīng)的Source Map文件,以方便在瀏覽器中能通過源碼調(diào)試。 控制Source Map輸出的Webpack 配置項是devtool,它有很多選項。
| devtool | 含義 |
|---|---|
| 空 | 不生成 Source Map |
eval |
每個 module 會封裝到 eval 里包裹起來執(zhí)行,并且會在每個eval語句的末尾追加注釋 //# sourceURL=webpack:///./main.js
|
source-map |
會額外生成一個單獨Source Map文件,并且會在JavaScript文件末尾追加 //# sourceMappingURL=bundle.js.map
|
hidden-source-map |
和source-map類似,但不會在JavaScript文件末尾追加 //# sourceMappingURL=bundle.js.map
|
inline-source-map |
和 source-map 類似,但不會額外生成一個單獨Source Map文件,而是把Source Map轉(zhuǎn)換成base64編碼內(nèi)嵌到JavaScript中 |
eval-source-map |
和 eval 類似,但會把每個模塊的Source Map轉(zhuǎn)換成base64編碼內(nèi)嵌到eval語句的末尾,例如 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW...
|
cheap-source-map |
和source-map類似,但生成的Source Map文件中沒有列信息,因此生成速度更快 |
cheap-module-source-map |
和cheap-source-map類似,但會包含Loader生成的Source Map |
其實以上表格只是列舉了devtool可能取值的一部分, 它的取值其實可以由source-map、eval、inline、hidden、cheap、module這六個關(guān)鍵字隨意組合而成。 這六個關(guān)鍵字每個都代表一種特性,它們的含義分別是:
-
eval:用eval語句包裹需要安裝的模塊; -
source-map:生成獨立的Source Map文件; -
hidden:不在JavaScript文件中指出Source Map文件所在,這樣瀏覽器就不會自動加載Source Map; -
inline:把生成的Source Map轉(zhuǎn)換成base64格式內(nèi)嵌在JavaScript文件中; -
cheap:生成的Source Map中不會包含列信息,這樣計算量更小,輸出的Source Map文件更??;同時Loader輸出的Source Map不會被采用; -
module:來自Loader的Source Map被簡單處理成每行一個模塊;
該如何選擇
Devtool配置項提供的這么多選項看似簡單,但很多人搞不清楚它們之間的差別和應(yīng)用場景。
如果你不關(guān)心細節(jié)和性能,只是想在不出任何差錯的情況下調(diào)試源碼,可以直接設(shè)置成 source-map,但這樣會造成兩個問題:
-
source-map模式下會輸出質(zhì)量最高最詳細的Source Map,這會造成構(gòu)建速度緩慢,特別是在開發(fā)過程需要頻繁修改的時候會增加等待時間; -
source-map模式下會把 Source Map 暴露出去,如果構(gòu)建發(fā)布到線上的代碼的Source Map暴露出去就等于源碼被泄露;
為了解決以上兩個問題,可以這樣做:
- 在開發(fā)環(huán)境下把
devtool設(shè)置成cheap-module-eval-source-map,因為生成這種 Source Map 的速度最快,能加速構(gòu)建。由于在開發(fā)環(huán)境下不會做代碼壓縮,Source Map 中即使沒有列信息也不會影響斷點調(diào)試; - 在生產(chǎn)環(huán)境下把
devtool設(shè)置成hidden-source-map,意思是生成最詳細的 Source Map,但不會把 Source Map 暴露出去。由于在生產(chǎn)環(huán)境下會做代碼壓縮,一個 JavaScript 文件只有一行,所以需要列信息。
在生產(chǎn)環(huán)境下通常不會把Source Map上傳到HTTP服務(wù)器讓用戶獲取,而是上傳到JavaScript錯誤收集系統(tǒng),在錯誤收集系統(tǒng)上根據(jù)Source Map和收集到的JavaScript運行錯誤堆棧計算出錯誤所在源碼的位置。
不要在生產(chǎn)環(huán)境下使用inline模式的Source Map, 因為這會使JavaScript文件變得很大,而且會泄露源碼。
加載現(xiàn)有的 Source Map
有些從Npm安裝的第三方模塊是采用ES6或者TypeScript編寫的,它們在發(fā)布時會同時帶上編譯出來的JavaScript文件和對應(yīng)的Source Map文件,以方便你在使用它們出問題的時候調(diào)試它們;
默認情況下Webpack是不會去加載這些附加的Source Map文件的,Webpack只會在轉(zhuǎn)換過程中生成 Source Map。 為了讓W(xué)ebpack加載這些附加的Source Map文件,需要安裝 source-map-loader 。 使用方法如下:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
// 只加載你關(guān)心的目錄下的 Source Map,以提升構(gòu)建速度
include: [path.resolve(root, 'node_modules/some-components/')],
use: ['source-map-loader'],
// 要把source-map-loader的執(zhí)行順序放到最前面,
// 如果在source-map-loader之前有Loader轉(zhuǎn)換了該 JavaScript文件,會導(dǎo)致Source Map映射錯誤
enforce: 'pre'
}
]
}
};
由于source-map-loader在加載Source Map時計算量很大,因此要避免讓該Loader處理過多的文件,不然會導(dǎo)致構(gòu)建速度緩慢。通常會采用include去命中只關(guān)心的文件。
再安裝新引入的依賴:
npm i -D source-map-loader
重啟Webpack后,你就能在瀏覽器中調(diào)試node_modules/some-components/目錄下的源碼了。