使用 NextJS 的自托管字体

Self-hosted fonts using NextJS

我正在尝试使用我的 NextJS 应用程序自行托管网络字体,但遇到了问题。 这是 URL 浏览器尝试访问这些字体:

localhost:3000/_next/static/css/fonts/Avenir.woff2

实际路径为:

_project_dir/static/fonts/Avenir.woff2

我尝试在 _app.js 中包含 link,它确实下载了字体,但文本仍然没有样式。

<link rel="preload" as="font" href="/static/fonts/Avenir.woff2" type="font/woff2" crossorigin />

这是我的 _app.js:


  render() {
    const { Component, pageProps } = this.props;
    return (
      <Container>
        <link href="https://fonts.googleapis.com/css?family=Poppins:500,500i,600&display=swap" rel="stylesheet" />
        <link rel="preload" as="font" href="/static/fonts/Avenir.woff2" type="font/woff2" crossorigin />
        <link rel="preload" as="font" href="/static/fonts/AvenirHeavy.woff2" type="font/woff2" crossorigin />
        <Head>
          <title>Project</title>
        </Head>
        <Provider store={store}>
          <PersistGate loading={null} persistor={persistor}>
            <Component pageContext={this.pageContext} {...pageProps} />
          </PersistGate>
        </Provider>
      </Container>
    );
  }
}

我的main.css

@font-face {
  font-family: 'Avenir';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('fonts/Avenir.eot');
  src: url('fonts/Avenir.eot?#iefix') format('embedded-opentype'), url('fonts/Avenir.woff2') format('woff2'),
    url('fonts/Avenir.woff') format('woff'), url('fonts/Avenir.ttf') format('truetype');
}

@font-face {
  font-family: 'Avenir';
  font-weight: 500;
  src: url('fonts/Avenir.eot');
  src: url('fonts/Avenir.eot?#iefix') format('embedded-opentype'), url('fonts/Avenir.woff2') format('woff2'),
    url('fonts/Avenir.woff') format('woff'), url('fonts/Avenir.ttf') format('truetype');
}

@font-face {
  font-family: 'Avenir';
  font-weight: 900;
  src: url('fonts/AvenirHeavy.eot');
  src: url('fonts/AvenirHeavy.eot?#iefix') format('embedded-opentype'), url('fonts/AvenirHeavy.woff2') format('woff2'),
    url('fonts/AvenirHeavy.woff') format('woff'), url('fonts/AvenirHeavy.ttf') format('truetype');
}

还有我的next.config.js

  webpack(config, options) {
    config.module.rules.push({
      test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
      use: {
        loader: 'url-loader',
        options: {
          limit: 100000,
        },
      },
    });
    return config;
  },

url('/static/fonts/Avenir.eot')

必须包含这样的路径。

在 next 的较新版本中,我相信您可以 import WOFF2 文件并在您的 CSS similar to this example for Gatsby. However, if you're not importing font files and instead placing them in the /static/fonts directory as you explain you can avoid using the WebPack loader or a plugin like next-fonts by hard-coding the font paths in your static directory :

中使用它
import React, { Fragment } from "react";

const WebFonts = () => (
  <Fragment>
    <style global jsx>{`
      @font-face {
        font-family: "Source Sans Pro";
        font-style: normal;
        font-stretch: normal;
        font-weight: 400;
        font-display: fallback;
        src: local("SourceSansPro Regular"), local("SourceSansPro-Regular"),
          url(/static/fonts/SourceSansPro-Regular.woff2) format("woff2");
        unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
          U+A720-A7FF;
      }
      @font-face {
        font-family: "Source Sans Pro";
        font-style: normal;
        font-weight: 600;
        font-display: fallback;
        src: local("SourceSansPro SemiBold"), local("SourceSansPro-SemiBold"),
          url(/static/fonts/SourceSansPro-SemiBold.woff2) format("woff2");
        unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
          U+A720-A7FF;
      }
      @font-face {
        font-family: "Source Sans Pro";
        font-style: normal;
        font-weight: 700;
        font-display: fallback;
        src: local("SourceSansPro SemiBold"), local("SourceSansPro-SemiBold"),
          url(/static/fonts/SourceSansPro-Bold.woff2) format("woff2");
        unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
          U+A720-A7FF;
      }
    `}</style>
  </Fragment>
);

export default WebFonts;

然后将该组件导入 NextJS 中的 _document 覆盖。这将使用添加到 NextJS 静态目录的字体。在提供更好的页面速度之前,请务必使用 woff2 提供的 woff2_compress 方法压缩从 Google 字体下载的所有 TTF 字体。如果您没有看到本地字体下载出现在 DevTools 的网络瀑布中,请确保通过删除这些字体的任何本地版本或 local 字体声明来进行测试。

你不需要任何额外的依赖来使用字体。只需将您的字体移动到 public/fonts 而不是 static/fonts.

然后预加载:

<link rel="preload" href="/fonts/Avenir.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />

记得保留开头的斜杠,否则将无法正常工作。根据 official docs:

Files inside public can then be referenced by your code starting from the base URL (/).

此外,在 JSX 中,它是 crossOrigin 而不是 crossorigin,并且它期望 string,而不是 Boolean

并且在 CSS/SCSS 中(向每个 src-url 添加开始斜杠):

@font-face {
  /* ... */
  src: url('/fonts/Avenir.woff2') format('woff2');
  /* ... */
}

然后去掉字体文件的自定义webpack配置。这种方法的缺点是字体不会像 url-loader 那样内联。但我认为无论如何内联所有字体都是非常低效的。 (您设置了 100000 的限制。几乎每个 woff/woff2 字体都会被内联。)

就我而言,我按照 brc-dd 所述设置了我的代码,但它仍然无法正常工作:字体已在网络选项卡中下载,但文本仍未设置样式。

最后,将我的字体从 .ttf 转换为 .woff2 解决了我的问题。

我的工作代码在 Next 11 中如下所示:

// document.js
<Head>
  <link
    rel="preload"
    href="/fonts/Lato/Lato-Light.woff2" // fix: convert .ttf to .woff2
    as="font"
    type="font/woff2"
    crossOrigin="anonymous"
  />
</Head>
// globals.css
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 300;
  font-display: optional;
  src: url("/fonts/Lato/Lato-Light.woff2") format("woff2");
}

我的字体存储在project_root/public/fonts/[fontname]/[fontfile].woff2