如何正确预加载文件名经过哈希处理且样式位于 CDN 中的字体?

How to correctly preload a font whose filename is hashed, and whose style is in a CDN?

我想预加载 Material 图标字体感谢:

<link rel="preload" href="https://fonts.gstatic.com/s/materialicons/v125/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2" as="font" type="font/woff2" crossorigin="anonymous">

事实上,它有效!

然而,在文件名中,我们有一个hash/UUID:v125/flUhRq6tzZclQEJ-Vdg-IuiaDsNc;因此,如果 Google 发布了一个新版本,例如 v126/sjboabchdiamblq-Abf-abvichef,那么我的预加载将不起作用!

有关更多详细信息,我使用他们的 CDN 是这样的:

<link
  href="https://fonts.googleapis.com/icon?family=Material+Icons"
  rel="stylesheet"
/>

哪个returns这个CSS:

@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/s/materialicons/v125/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
}

.material-icons {
  font-family: 'Material Icons';
  [...]
}

注意:我正在使用 Angular,我希望立即开始加载字体,而不是在加载应用程序时开始,即当 Material 图标为 [=28] 时=],即使 CSS 文件立即加载,字体也不会开始加载,直到显示 Material 图标。

我找到了解决方案,感谢 https://www.npmjs.com/package/@angular-builders/custom-webpack, and using a library for mMaterial Icons (https://www.npmjs.com/package/material-icons)

此构建器允许在构建过程后转换 index.html。

  1. 在 index.html 中添加经典预加载(在开发中有效)
<link rel="preload" href="/material-icons.woff2" as="font" type="font/woff2" crossorigin="anonymous">
<link rel="preload" href="/material-icons-outlined.woff2" as="font" type="font/woff2" crossorigin="anonymous">
  1. 像这样在 angular.json 中添加 "indexTransform": "index-html-transform.js"
"configurations": {
  "production": {
    [...],
    "indexTransform": "index-html-transform.js"
  }
}
  1. 创建index-html-transform.js:
const fs = require("fs");

/**
 * @param {string} indexHtmlSource
 * @return {string}
 */
const setMaterialIconsFontsPreloading = (indexHtmlSource) => {
  const allOutputFilenames = fs.readdirSync("./dist/essai-preload-mat-icons");

  const requiredMatIconsFontsMatches = indexHtmlSource.matchAll(/(material-icons.*)\.woff2/g);

  /**
   * @exemple `['material-icons', 'material-icons-outlined']`
   */
  const requiredIconTypes = [...requiredMatIconsFontsMatches].map((match) => match[1]);

  return requiredIconTypes.reduce((previousIndexHtml, requiredIconType) => {
    /**
     * @exemple `'material-icons-outlined.woff2'`
     */
    const inputFilename = `${requiredIconType}.woff2`;

    /**
     * @exemple `'material-icons-outlined.125af8545b6.woff2'`
     */

    const outputFilename = allOutputFilenames.find((outputFilenameItem) => outputFilenameItem.match(`^${requiredIconType}\.\w+\.woff2$`));

    return previousIndexHtml.replace(inputFilename, outputFilename);
  }, indexHtmlSource);
}

/**
 *
 * @param {{ configuration?: string; project: string; target: string;}} targetOptions
 * @param {string} indexHtmlSource
 * @returns {string} The final index.html to serve.
 */
module.exports = (targetOptions, indexHtmlSource) => {
    return setMaterialIconsFontsPreloading(indexHtmlSource);
};