使用 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
我正在尝试使用我的 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