Webpack實戰(zhàn)——加載資源

加載圖片

使用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)化:

以上加載圖片的方法同樣適用于其它二進制類型的資源,例如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、inlinehidden、cheapmodule這六個關(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/目錄下的源碼了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 webpack介紹和使用 一、webpack介紹 1、由來 ...
    it筱竹閱讀 11,462評論 0 21
  • webpack 是什么? 本質(zhì)上,webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(mo...
    IT老馬閱讀 3,562評論 2 27
  • 學(xué)習(xí)流程 參考文檔:入門Webpack,看這篇就夠了Webpack for React 一. 簡單使用webpac...
    Jason_Zeng閱讀 3,260評論 2 16
  • 前端將大型項目分成一個個單獨的模塊,一般封裝好的每個模塊都會實現(xiàn)一個目的明確的完成的功能。如何處理這些模塊以及模塊...
    pixels閱讀 3,509評論 1 14
  • 讓世界因我更閃亮,明光 20161021 今天的誦讀是在大課間舉行的,因為吃過早飯后課代表忘了發(fā)晨誦作業(yè),我們沒有...
    河南麥子的書寫閱讀 656評論 0 1

友情鏈接更多精彩內(nèi)容