前面我們已經提及如何使用 webpack 來滿足不同環境的構建需求,其中在生產環境構建時會做額外的一些工作,例如代碼壓縮等。這一部分的工作就是這一小節的主題,即優化前端資源的加載性能。
我們總是希望瀏覽器在加載頁面時用的時間越短越好,所以構建出來的文件應該越少越小越好,一來減少瀏覽器需要發起請求的數量,二來減少下載靜態資源的時間。
其實 webpack 把多個代碼文件打包成幾個必需的靜態資源,已經很大程度減少了靜態資源請求數量了,接下來我們來介紹下如何使用 webpack 實現更多的前端資源加載的優化需求。
## CSS Sprites
[CSS Sprites](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Images/Implementing_image_sprites_in_CSS) 技術是前端領域一種很常見的用于減少圖片資源請求數的優化方式,這里不做詳細的介紹。
> 在了解 webpack 配置之前,需要明白 CSS Sprites 的原理。
如果你使用的 webpack 3.x 版本,需要 CSS Sprites 的話,可以使用 [webpack-spritesmith](https://github.com/mixtur/webpack-spritesmith) 或者 [sprite-webpack-plugin](https://github.com/kezoo/sprite-webpack-plugin)。
我們以 webpack-spritesmith 為例,先安裝依賴:
```
npm install webpack-spritesmith --save-dev
```
在 webpack 的配置中應用該插件:
```
module: {
loaders: [
// ... 這里需要有處理圖片的 loader,如 file-loader
]
},
resolve: {
modules: [
'node_modules',
'spritesmith-generated', // webpack-spritesmith 生成所需文件的目錄
],
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'), // 多個圖片所在的目錄
glob: '*.png' // 匹配圖片的路徑
},
target: {
// 生成最終圖片的路徑
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
// 生成所需 SASS/LESS/Stylus mixins 代碼,我們使用 Stylus 預處理器做例子
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl'),
},
apiOptions: {
cssImageRef: "~sprite.png"
},
}),
],
```
在你需要的樣式代碼中引入 `sprite.styl` 后調用需要的 mixins 即可:
```
@import '~sprite.styl'
.close-button
sprite($close)
.open-button
sprite($open)
```
更多的 webpack-spritesmith 配置可以參考:[Config of webpack-spritesmith](https://github.com/mixtur/webpack-spritesmith#config)。
遺憾的是,上面提到的這兩個 plugin 還沒更新到支持 webpack 4.x 版本,如果你使用的是 webpack 4.x,你需要配合使用 [postcss](https://github.com/postcss/postcss) 和 [postcss-sprites](https://github.com/2createStudio/postcss-sprites),才能實現 CSS Sprites 的相關構建。
## 圖片壓縮
在一般的項目中,圖片資源會占前端資源的很大一部分,既然代碼都進行壓縮了,占大頭的圖片就更不用說了。
我們之前提及使用 file-loader 來處理圖片文件,在此基礎上,我們再添加一個 [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader) 來壓縮圖片文件。簡單的配置如下:
```
module.exports = {
// ...
module: {
rules: [
{
test: /.*\.(gif|png|jpe?g|svg|webp)$/i,
use: [
{
loader: 'file-loader',
options: {}
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: { // 壓縮 jpeg 的配置
progressive: true,
quality: 65
},
optipng: { // 使用 imagemin-optipng 壓縮 png,enable: false 為關閉
enabled: false,
},
pngquant: { // 使用 imagemin-pngquant 壓縮 png
quality: '65-90',
speed: 4
},
gifsicle: { // 壓縮 gif 的配置
interlaced: false,
},
webp: { // 開啟 webp,會把 jpg 和 png 圖片壓縮為 webp 格式
quality: 75
},
},
],
},
],
},
}
```
image-webpack-loader 的壓縮是使用 [imagemin](https://github.com/imagemin) 提供的一系列圖片壓縮類庫來處理的,如果需要進一步了解詳細的配置,可以查看對應類庫的官方文檔 [usage of image-webpack-loader](https://github.com/tcoopman/image-webpack-loader#usage)。
## 使用 DataURL
有的時候我們的項目中會有一些很小的圖片,因為某些緣故并不想使用 CSS Sprites 的方式來處理(譬如小圖片不多,因此引入 CSS Sprites 感覺麻煩),那么我們可以在 webpack 中使用 url-loader 來處理這些很小的圖片。
[url-loader](https://github.com/webpack-contrib/url-loader) 和 [file-loader](https://github.com/webpack-contrib/file-loader) 的功能類似,但是在處理文件的時候,可以通過配置指定一個大小,當文件小于這個配置值時,url-loader 會將其轉換為一個 base64 編碼的 DataURL,配置如下:
```
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, // 單位是 Byte,當文件小于 8KB 時作為 DataURL 處理
},
},
],
},
],
},
}
```
更多關于 url-loader 的配置可以參考官方文檔 [url-loader](https://github.com/webpack-contrib/url-loader),一般情況僅使用 `limit` 即可。
## 代碼壓縮
webpack 4.x 版本運行時,mode 為 production 即會啟動壓縮 JS 代碼的插件,而對于 webpack 3.x,使用壓縮 JS 代碼插件的方式也已經介紹過了。在生產環境中,壓縮 JS 代碼基本是一個必不可少的步驟,這樣可以大大減小 JavaScript 的體積,相關內容這里不再贅述。
除了 JS 代碼之外,我們一般還需要 HTML 和 CSS 文件,這兩種文件也都是可以壓縮的,雖然不像 JS 的壓縮那么徹底(替換掉長變量等),只能移除空格換行等無用字符,但也能在一定程度上減小文件大小。在 webpack 中的配置使用也不是特別麻煩,所以我們通常也會使用。
對于 HTML 文件,之前介紹的 html-webpack-plugin 插件可以幫助我們生成需要的 HTML 并對其進行壓縮:
```
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 配置輸出文件名和路徑
template: 'assets/index.html', // 配置文件模板
minify: { // 壓縮 HTML 的配置
minifyCSS: true, // 壓縮 HTML 中出現的 CSS 代碼
minifyJS: true // 壓縮 HTML 中出現的 JS 代碼
}
}),
],
}
```
如上,使用 `minify` 字段配置就可以使用 HTML 壓縮,這個插件是使用 [html-minifier](https://github.com/kangax/html-minifier#options-quick-reference) 來實現 HTML 代碼壓縮的,`minify` 下的配置項直接透傳給 html-minifier,配置項參考 html-minifier 文檔即可。
對于 CSS 文件,我們之前介紹過用來處理 CSS 文件的 css-loader,也提供了壓縮 CSS 代碼的功能:
```
module.exports = {
module: {
rules: [
// ...
{
test: /\.css/,
include: [
path.resolve(__dirname, 'src'),
],
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true, // 使用 css 的壓縮功能
},
},
],
},
],
}
}
```
在 css-loader 的選項中配置 `minimize` 字段為 `true` 來使用 CSS 壓縮代碼的功能。css-loader 是使用 [cssnano](http://cssnano.co/) 來壓縮代碼的,`minimize` 字段也可以配置為一個對象,來將相關配置傳遞給 cssnano。更多詳細內容請參考 [cssnano](http://cssnano.co/) 官方文檔。
## 小結
由于優化前端資源加載這個主題相關的內容比較多,所以拆分成多個小節。本小節先介紹了比較基礎的部分:CSS Sprites、圖片壓縮、使用 DataURL,以及基本的代碼壓縮,接下來的第 10、11 小節還會繼續圍繞前端資源加載優化的這個主題,介紹更加深入的內容。
## 例子
本小節提及的一些簡單的 Demo 可以在 [webpack-examples](https://github.com/teabyii/webpack-examples) 找到。