資源模組

資源模組允許使用資源檔案(字型、圖示等),而無需設定其他載入器。

在 webpack 5 之前,通常使用

資源模組類型透過新增 4 個新的模組類型取代所有這些載入器

  • asset/resource 發出一個獨立的檔案並匯出 URL。以前可透過使用 file-loader 達成。
  • asset/inline 匯出資源的資料 URI。以前可透過使用 url-loader 達成。
  • asset/source 匯出資源的原始碼。以前可透過使用 raw-loader 達成。
  • asset 會自動在匯出資料 URI 和發出獨立檔案之間進行選擇。以前可透過使用具有資源大小限制的 url-loader 達成。

在 webpack 5 中使用舊資產載入器(例如 file-loader/url-loader/raw-loader)與資產模組時,您可能希望停止資產模組再次處理您的資產,因為這將導致資產重複。這可透過將資產的模組類型設定為 'javascript/auto' 來完成。

webpack.config.js

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

若要將來自新 URL 呼叫的資產排除在資產載入器之外,請將 dependency: { not: ['url'] } 加入載入器組態。

webpack.config.js

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

公開路徑

在預設情況下,asset 類型在幕後執行 __webpack_public_path__ + import.meta。這表示在您的組態中設定 output.publicPath 將允許您覆寫 asset 載入的 URL。

即時覆寫

如果您在程式碼中設定 __webpack_public_path__,則您需要達成的目標是確保在您的應用程式中將其作為第一個程式碼執行,且不使用函式執行,否則將會中斷 asset 載入邏輯。這方面的範例是有一個名為 publicPath.js 的檔案,其內容為

__webpack_public_path__ = 'https://cdn.url.com';

然後在您的 webpack.config.js 中,更新您的 entry 欄位,使其看起來像

module.exports = {
  entry: ['./publicPath.js', './App.js'],
};

或者,您可以在您的 App.js 中執行下列動作,而無需修改您的 webpack 組態。唯一的缺點是您必須在此強制執行順序,而這可能會與某些程式碼檢查工具發生衝突。

import './publicPath.js';

資源資產

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 檔案都將發射到輸出目錄,且其路徑將注入到套件中,此外,您可以為它們自訂 outputPathpublicPath

自訂輸出檔名

預設情況下,asset/resource 模組會以 [hash][ext][query] 檔名發射到輸出目錄。

你可以透過在 webpack 設定中設定 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'
      }
    ]
  },
};

自訂輸出檔名的另一個情況是將某種類型的資產發射到指定的目錄

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 檔案都會發射到輸出目錄中的 static 目錄。

Rule.generator.filenameoutput.assetModuleFilename 相同,而且只適用於 assetasset/resource 模組類型。

內嵌資產

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 檔案都會以資料 URI 的形式注入到套件中。

自訂資料 URI 產生器

預設情況下,webpack 發射的資料 URI 會使用 Base64 演算法對檔案內容進行編碼。

如果你想使用自訂編碼演算法,你可以指定一個自訂函式來對檔案內容進行編碼

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);
+         }
+       }
      }
    ]
  },
};

現在所有 .svg 檔案都會由 mini-svg-data-uri 套件進行編碼。

原始資產

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 檔案都會原樣注入到套件中。

URL 資產

當使用 new URL('./path/to/asset', import.meta.url) 時,webpack 也會建立一個資產模組。

src/index.js

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

根據設定中的 target,webpack 會將上述程式碼編譯成不同的結果

// 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 開始,資料 URL 也支援 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',
      }
    ]
  },
};

現在 webpack 會自動根據預設條件在 resourceinline 之間進行選擇:大小小於 8kb 的檔案將被視為 inline 模組類型,否則為 resource 模組類型。

您可以在 webpack 組態的模組規則層級上設定 Rule.parser.dataUrlCondition.maxSize 選項來變更此條件

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
+         }
+       }
      }
    ]
  },
};

您也可以 指定一個函式 來決定是否要將模組內嵌。

取代內嵌載入器語法

在資產模組和 Webpack 5 之前,可以使用 內嵌語法 搭配上述舊載入器。

現在建議移除所有內嵌載入器語法,並使用 resourceQuery 條件來模擬內嵌語法的功能。

例如,在用 asset/source 類型取代 raw-loader 的情況下

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

以及在 webpack 組態中

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

如果您想排除未處理的原始資產由其他載入器處理,請使用否定條件

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

oneOf 規則清單。在此,只會套用第一個符合的規則

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

停用發射資產

對於伺服器端渲染等使用案例,您可能想要停用發射資產,這可以使用 Rule.generator 下的 emit 選項來達成

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

6 貢獻者

smelukovEugeneHlushkochenxsananshumanvspence-sdkdk225