webpack5資源最佳加載方案

在前面幾篇文章中,我們已經(jīng)學(xué)會基礎(chǔ)的運用webpack與webpack-cli從0到1搭建一個簡單的react或者vue工程應(yīng)用,這其中我們使用了加載文件,我們在之前處理文件使用file-loader或者url-loader處理,url-loader主要是可以針對圖片文件大小進行有選擇的base64壓縮,在webpack5中可以用內(nèi)置的Asset Modules來處理圖片資源
接下來我們一起探討學(xué)習(xí)下webpack5中關(guān)于Asset Modules[1]的那些事

正文開始...

初始化基礎(chǔ)項目
新建一個文件夾webpack-04-resource,

npm init -y
我們安裝項目一些基礎(chǔ)支持的插件

npm i webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel
l/core -D
在根目錄新建webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/env']
        }
      },
      {
        test: /\.(png|jpg)$/i,
        type: 'asset/resource'
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
  ]
};
注意我們加載圖片沒有使用file-loader與url-loader,我們使用的是webpack5內(nèi)置的asset/rosource這個來處理

  module.exports = {
    module: {
      rules: [
        {
          test: /\.(png|jpg)$/i,
          type: 'asset/resource'
        }
      ]
    }
  }
在index.js中我們插入一張圖片

import img1Src from '../assets/images/1.png';
var appDom = document.getElementById('app');
const img = new Image();
img.src = img1Src;
appDom.appendChild(img);
ok,運行npm run server,打開瀏覽器localhost:8080

我們會發(fā)現(xiàn),生成的圖片地址就是<img src="http://localhost:8080/js/../b1640e009cff6a775ce5.png">

generator配置
現(xiàn)在我想配置圖片的默認(rèn)輸出地址與名字,在module.rules中有一個generator的屬性可以配置匹配圖片輸出的文件

// webpack.config.js
 module.exports = {
    module: {
      rules: [
        ...
        {
          test: /\.(png|jpg)$/i,
          type: 'asset/resource',
          generator: {
            filename: 'images/[name][ext]'
          }
        }
      ]
    }
  }
此時頁面加載圖片的路徑就變成<img src="http://localhost:8080/js/../images/1.png">了






如果你的圖片地址是上傳到cdn上的,那么你可以像下面一樣這么做,但是這種做法是不是在項目中真的需要,還有待商榷,因為這樣會導(dǎo)致應(yīng)用的所有圖片用cdn方式加載,如果項目中只是部分圖片按需cdn加載,那么這種做法是不可取的。

 {
        test: /\.(png|jpg)$/i,
        // type: 'asset/resource'
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 40 * 1024
          }
        },
        generator: {
          publicPath: 'https://cdn/assets', // cdn域名前綴
          filename: 'images/[name][ext]'
        }
      }
自此頁面的加載的圖片就是<img src="https://cdn/assets/images/3.png">

assetModuleFilename

除了generator.filename方式,你也可以在output中加入assetModuleFilename配置來修改圖片默認(rèn)的地址,不過注意這個屬性只能是針對rule中設(shè)置的type''asset/resource' | 'asset'類型才生效。

module.exports = {
  output: {
    filename: 'js/[name].js',
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'images/[name][ext]'
  }
}
通常項目里我們會把比較小的圖片直接坐base64加載,大的圖片就直接輸出加載,或者上傳到cdn直接加載圖片地址,你可以在rules的generator.publicPath設(shè)置地址圖片地址。

因此我引入兩張大小不一樣的圖片測試,修改一下index.js

import img1Src from '../assets/images/1.png';
import img3Src from '../assets/images/3.png';

function renderImage(imageSource) {
  const weakMap = new WeakMap();
  var appDom = document.getElementById('app');
  imageSource.forEach((src) => {
    const img = new Image();
    weakMap.set(img, img);
    if (weakMap.has(img)) {
      weakMap.get(img).src = src;
      appDom.appendChild(img);
    }
  });
}
renderImage([img1Src, img3Src]);
我們再修改下webpack.config.js

module.exports = {
  module: {
    rules: [
       ...
       {
        test: /\.(png|jpg)$/i,
        // type: 'asset/resource'
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 40 * 1024
          }
        }
      }
    ]
  }
}
在rules中增加parser屬性,并且將type改成asset,當(dāng)我們設(shè)置一個dataUrlCondition: {maxSize: 40 * 1024},小于KB就用base64加載了,大于40KB就直接用圖片路徑加載



因此我們可以看到兩張圖片,一張圖片是base64一張圖片就走文件路徑了。

所以在你的項目中你可以利用這個parser.dataUrlCondition.maxSize特性來優(yōu)化圖片資源,有些資源小圖片就可以用base64來加載,這樣可以減少頁面圖片的資源請求

但是并不是所有的圖片都要base64,base64生成的字符串非常大,同時也是增加了html的體積,無法利用緩存機制加載圖片。

所以在優(yōu)化的網(wǎng)頁加載過程中,并不是全部都用base64來加載圖片。

關(guān)于內(nèi)置模塊的幾個參數(shù)
主要參考官網(wǎng)asset-modules[2]

webpack5之前

row-loader 將文件導(dǎo)入為字符串,比如導(dǎo)入.txt類型的文件
url-loader 將文件作為Data Url嵌入到打包后bundle.js中,比如base64文件
file-loader 將文件輸出目錄,圖片文件會被打包到指定目錄中加載
webpack5現(xiàn)在

用asset module type通過添加以下四種類型來代替以上loader

asset/resource 導(dǎo)出單獨的url,是file-loader的替代品
asset/inline 導(dǎo)出資源Data Url,是url-loader的替代品
asset/source 到處文件資源內(nèi)容,是row-loader的替代品
asset 在url-loader和file-loader中選擇,配置parse.dataUrlCondition.maxSize來輸出圖片資源是否base64輸出
總結(jié)
相比較webpack5之前我們加載圖片資源文件使用file-loader或者url-loader在webpack5中可以使用內(nèi)置模塊type: 'assets/resource'

基于webpack5內(nèi)置模塊asset module type來設(shè)置資源的加載

圖片資源base64處理,根據(jù)圖片資源的大小parse.dataUrlCondition.maxSize來限制是否需要base64輸出

比較asset module type幾種模式區(qū)別,代替以前row-loader、file-loader、url-loader方案,但是這僅僅是你的webpack版本在5以后。

本文code example[3]

參考資料
[1]
Asset Modules:
https://webpack.docschina.org/guides/asset-modules/

[2]
asset-modules:
https://webpack.docschina.org/guides/asset-modules/

[3]
code example:
https://github.com/maicFir/lessonNote/tree/master/webpack/webpack-04-assets









作者:Maic

歡迎關(guān)注微信公眾號 :web技術(shù)學(xué)苑