Webpack 資源模塊

2023-05-19 17:04 更新

資源模塊(asset module)是一種模塊類型,它允許使用資源文件(字體,圖標(biāo)等)而無需配置額外 loader。

在 webpack 5 之前,通常使用:

  • ?raw-loader? 將文件導(dǎo)入為字符串
  • ?url-loader? 將文件作為 data URI 內(nèi)聯(lián)到 bundle 中
  • ?file-loader? 將文件發(fā)送到輸出目錄

資源模塊類型(asset module type),通過添加 4 種新的模塊類型,來替換所有這些 loader:

  • ?asset/resource? 發(fā)送一個(gè)單獨(dú)的文件并導(dǎo)出 URL。之前通過使用 ?file-loader? 實(shí)現(xiàn)。
  • ?asset/inline? 導(dǎo)出一個(gè)資源的 data URI。之前通過使用 ?url-loader? 實(shí)現(xiàn)。
  • ?asset/source? 導(dǎo)出資源的源代碼。之前通過使用 ?raw-loader? 實(shí)現(xiàn)。
  • ?asset? 在導(dǎo)出一個(gè) data URI 和發(fā)送一個(gè)單獨(dú)的文件之間自動(dòng)選擇。之前通過使用 ?url-loader?,并且配置資源體積限制實(shí)現(xiàn)。

當(dāng)在 webpack 5 中使用舊的 assets loader(如 ?file-loader?/?url-loader?/?raw-loader? 等)和 asset 模塊時(shí),你可能想停止當(dāng)前 asset 模塊的處理,并再次啟動(dòng)處理,這可能會(huì)導(dǎo)致 asset 重復(fù),你可以通過將 asset 模塊的類型設(shè)置為 ?'javascript/auto'? 來解決。

webpack.config.js

module.exports = {
  module: {
   rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            }
          },
        ],
+       type: 'javascript/auto'
      },
   ]
  },
}

如需從 asset loader 中排除來自新 URL 處理的 asset,請?zhí)砑?nbsp;?dependency: { not: ['url'] }? 到 loader 配置中。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
+       dependency: { not: ['url'] },
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
    ],
  }
}

Resource 資源

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
+ module: {
+   rules: [
+     {
+       test: /\.png/,
+       type: 'asset/resource'
+     }
+   ]
+ },
};

src/index.js

import mainImage from './images/main.png';

img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'

所有 ?.png? 文件都將被發(fā)送到輸出目錄,并且其路徑將被注入到 bundle 中,除此之外,你可以為它們自定義 ?outputPath? 和 ?publicPath? 屬性。

自定義輸出文件名

默認(rèn)情況下,?asset/resource? 模塊以 ?[hash][ext][query]? 文件名發(fā)送到輸出目錄。

可以通過在 webpack 配置中設(shè)置 ?output.assetModuleFilename? 來修改此模板字符串:

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource'
      }
    ]
  },
};

另一種自定義輸出文件名的方式是,將某些資源發(fā)送到指定目錄:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource'
-     }
+     },
+     {
+       test: /\.html/,
+       type: 'asset/resource',
+       generator: {
+         filename: 'static/[hash][ext][query]'
+       }
+     }
    ]
  },
};

使用此配置,所有 ?html? 文件都將被發(fā)送到輸出目錄中的 ?static? 目錄中。

?Rule.generator.filename? 與 ?output.assetModuleFilename? 相同,并且僅適用于 ?asset? 和 ?asset/resource? 模塊類型。

inline 資源(inlining asset)

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
-   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
-       test: /\.png/,
-       type: 'asset/resource'
+       test: /\.svg/,
+       type: 'asset/inline'
-     },
+     }
-     {
-       test: /\.html/,
-       type: 'asset/resource',
-       generator: {
-         filename: 'static/[hash][ext][query]'
-       }
-     }
    ]
  }
};

src/index.js

- import mainImage from './images/main.png';
+ import metroMap from './images/metro.svg';

- img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
+ block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)

所有 ?.svg? 文件都將作為 data URI 注入到 bundle 中。

自定義 data URI 生成器

webpack 輸出的 data URI,默認(rèn)是呈現(xiàn)為使用 Base64 算法編碼的文件內(nèi)容。

如果要使用自定義編碼算法,則可以指定一個(gè)自定義函數(shù)來編碼文件內(nèi)容:

webpack.config.js

const path = require('path');
+ const svgToMiniDataURI = require('mini-svg-data-uri');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.svg/,
        type: 'asset/inline',
+       generator: {
+         dataUrl: content => {
+           content = content.toString();
+           return svgToMiniDataURI(content);
+         }
+       }
      }
    ]
  },
};

現(xiàn)在,所有 ?.svg? 文件都將通過 ?mini-svg-data-uri? 包進(jìn)行編碼。

source 資源(source asset)

webpack.config.js

const path = require('path');
- const svgToMiniDataURI = require('mini-svg-data-uri');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
-       test: /\.svg/,
-       type: 'asset/inline',
-       generator: {
-         dataUrl: content => {
-           content = content.toString();
-           return svgToMiniDataURI(content);
-         }
-       }
+       test: /\.txt/,
+       type: 'asset/source',
      }
    ]
  },
};

src/example.txt

Hello world

src/index.js

- import metroMap from './images/metro.svg';
+ import exampleText from './example.txt';

- block.style.background = `url(${metroMap}); // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
+ block.textContent = exampleText; // 'Hello world'

所有 .txt 文件將原樣注入到 bundle 中。

URL 資源

當(dāng)使用 ?new URL('./path/to/asset', import.meta.url)?,webpack 也會(huì)創(chuàng)建資源模塊。

src/index.js

const logo = new URL('./logo.svg', import.meta.url);

根據(jù)你配置中 ?target? 的不同,webpack 會(huì)將上述代碼編譯成不同結(jié)果:

// target: web
new URL(
  __webpack_public_path__ + 'logo.svg',
  document.baseURI || self.location.href
);

// target: webworker
new URL(__webpack_public_path__ + 'logo.svg', self.location);

// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
new URL(
  __webpack_public_path__ + 'logo.svg',
  require('url').pathToFileUrl(__filename)
);

自 webpack 5.38.0 起,Data URLs 也支持在 ?new URL()? 中使用了:

src/index.js

const url = new URL('data:,', import.meta.url);
console.log(url.href === 'data:,');
console.log(url.protocol === 'data:');
console.log(url.pathname === ',');

通用資源類型

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
+       test: /\.txt/,
+       type: 'asset',
      }
    ]
  },
};

現(xiàn)在,webpack 將按照默認(rèn)條件,自動(dòng)地在 ?resource? 和 ?inline? 之間進(jìn)行選擇:小于 8kb 的文件,將會(huì)視為 ?inline? 模塊類型,否則會(huì)被視為 ?resource? 模塊類型。

可以通過在 webpack 配置的 module rule 層級中,設(shè)置 ?Rule.parser.dataUrlCondition.maxSize? 選項(xiàng)來修改此條件:

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.txt/,
        type: 'asset',
+       parser: {
+         dataUrlCondition: {
+           maxSize: 4 * 1024 // 4kb
+         }
+       }
      }
    ]
  },
};

還可以 指定一個(gè)函數(shù) 來決定是否 inline 模塊。

變更內(nèi)聯(lián) loader 的語法

在 asset 模塊和 webpack 5 之前,可以使用內(nèi)聯(lián)語法與上述傳統(tǒng)的 loader 結(jié)合使用。

現(xiàn)在建議去掉所有的內(nèi)聯(lián) loader 的語法,使用資源查詢條件來模仿內(nèi)聯(lián)語法的功能。

示例,將 ?raw-loader? 替換為 ?asset/source? 類型:

- import myModule from 'raw-loader!my-module';
+ import myModule from 'my-module?raw';

webpack 相關(guān)配置:

module: {
    rules: [
    // ...
+     {
+       resourceQuery: /raw/,
+       type: 'asset/source',
+     }
    ]
  },

如果你想把原始資源排除在其他 loader 的處理范圍以外,請使用使用取反的正則:

module: {
    rules: [
    // ...
+     {
+       test: /\.m?js$/,
+       resourceQuery: { not: [/raw/] },
+       use: [ ... ]
+     },
      {
        resourceQuery: /raw/,
        type: 'asset/source',
      }
    ]
  },

或者使用 ?oneOf? 的規(guī)則列表。此處只應(yīng)用第一個(gè)匹配規(guī)則:

module: {
    rules: [
    // ...
+     { oneOf: [
        {
          resourceQuery: /raw/,
          type: 'asset/source',
        },
+       {
+         test: /\.m?js$/,
+         use: [ ... ]
+       },
+     ] }
    ]
  },

禁止生成資源

對于像服務(wù)器端渲染這樣的用例,若是希望禁止生成資源,可以通過在? Rule.generator? 下使用? emit? 選項(xiàng)來實(shí)現(xiàn)。

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.png$/i,
        type: 'asset/resource',
        generator: {
          emit: false,
        },
      },
    ],
  },
};

Further Reading


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號