Next.js生产构建时如何保留UI库中的字体引用?
我们团队正在开发一个Next.js应用,依赖另一团队提供的共享UI库(the-ui-library)。该库通过CSS的@font-face定义了Roboto字体:
@font-face { font-family: 'Roboto'; src: url('./Roboto/Roboto-VariableFont_wdth,wght.woff2') format('woff2'), url('./Roboto/Roboto-VariableFont_wdth,wght.ttf') format('truetype'); font-weight: 100 300 400 500 700 900; font-style: normal; font-display: swap; }
本地运行时字体显示正常,但构建部署到服务器后字体无法加载:字体文件存在于Next.js构建产物中,但@font-face规则未被包含;组件仍引用该字体,但浏览器因无来源引用,不会发起加载请求,控制台无相关报错,网络请求中也无字体加载记录。
当前未使用Next.js的SSR功能,简化后的Next.js配置如下:
/** * @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, distDir: 'build', output: 'export', trailingSlash: true, assetPrefix: process.env.NODE_ENV === 'development' ? '' : '/cdn', transpilePackages: [ 'redacted-1', 'redacted-2', 'the-ui-library', ], eslint: { dirs: [ 'contexts', 'components', 'hooks', 'models', 'pages', 'routes', 'styles', 'utils', '__tests__', ], }, images: { unoptimized: true, }, webpack: (config) => { // 无关内部代码已移除 return config; }, }; export default nextConfig;
检查UI库的CSS打包配置
确认the-ui-library的package.json中是否指定了style字段,指向库的核心CSS文件。如果未指定,Next.js可能无法自动识别并处理库中的CSS规则,导致@font-face丢失。添加该字段后重新构建测试:// the-ui-library/package.json { "style": "dist/styles.css" // 替换为实际CSS文件路径 }调整Webpack字体与CSS解析规则
在Next.js的webpack配置中添加字体文件的处理规则,并确保CSS中的相对字体路径被正确解析:const path = require('path'); const nextConfig = { // 其他配置... webpack: (config) => { // 处理woff2/ttf字体文件,输出到指定目录 config.module.rules.push({ test: /\.(woff2|ttf)$/, type: 'asset/resource', generator: { filename: 'fonts/[name].[hash][ext]' } }); // 解析CSS中指向UI库字体的相对路径 config.module.rules.forEach(rule => { if (rule.test?.toString().includes('css')) { rule.resolve = { alias: { './Roboto': path.resolve(__dirname, 'node_modules/the-ui-library/path/to/Roboto') } }; } }); return config; }, };注意将
path/to/Roboto替换为UI库中字体文件的实际存放路径。手动导入UI库CSS
在Next.js的全局CSS文件(如styles/globals.css)中手动导入UI库的CSS,确保构建时该文件被纳入产物:@import 'the-ui-library/dist/styles.css';需确认UI库的dist目录下存在对应CSS文件,路径无误。
验证assetPrefix对路径的影响
由于设置了assetPrefix: '/cdn',需确保字体路径最终会带上该前缀。可通过修改webpack的publicPath配置实现:const nextConfig = { // 其他配置... webpack: (config) => { config.output.publicPath = process.env.NODE_ENV === 'development' ? '' : '/cdn/'; return config; }, };手动复制字体到公共目录
如果静态导出逻辑未自动收集node_modules中的字体,可手动将UI库的字体文件复制到Next.js的public/fonts目录,然后在全局CSS中重新定义@font-face:@font-face { font-family: 'Roboto'; src: url('/cdn/fonts/Roboto-VariableFont_wdth,wght.woff2') format('woff2'), url('/cdn/fonts/Roboto-VariableFont_wdth,wght.ttf') format('truetype'); font-weight: 100 900; font-style: normal; font-display: swap; }
内容的提问来源于stack exchange,提问作者Brandon D

