在Next.js项目中使用本地字体本是一件简单的事,但不少团队把代码推到Vercel之后,页面样式里的自定义字体突然失效,控制台提示字体文件无法解析或404。这类问题通常不是代码逻辑错误,而是构建与部署环节的资源处理差异导致的。
一、本地字体在Vercel失败的常见原因
Next.js提供了next/font/local来加载项目内的字体文件,在本地开发时,由于服务直接从磁盘读取,只要路径写对就能正常显示。但Vercel的构建是一个隔离的云端环境,它会根据依赖图和配置来决定哪些文件进入最终部署包。如果字体文件位于未被纳入编译管线的目录,或者引入方式不符合规范,构建器就不会把它们复制到输出目录。
另一个容易被忽略的点是.gitignore或vercel.json的配置。有些项目把fonts/目录加进了忽略列表,导致Vercel拉取代码时根本拿不到字体源文件。还有人把字体放在src外层的临时目录,本地能用是因为绝对路径生效,线上构建一重定向就丢了引用。
1.1 构建日志里的线索
遇到解析失败,第一步应查看Vercel的构建日志。搜索字体文件名,若完全没有出现拷贝记录,说明它没被识别为资源。相比之下,本地终端运行next build有时也会暴露同样问题,但开发者常跳过这一步直接部署。
我们可以通过在构建后检查.next目录来确认。若.next/static/media下没有对应的字体文件,就证明打包阶段已经漏掉了它们,线上自然无法解析。
二、标准且稳妥的本地字体用法
官方推荐把字体文件放在app或pages同级的可访问路径,并使用next/font/local声明。下面是一段正确的示例代码,字体放在app/fonts目录中。
// app/fonts/loader.js
import localFont from 'next/font/local';
// 引入本地字体,路径相对于当前文件
export const myCustomFont = localFont({
src: './MyFont-Regular.woff2',
fontWeight: 400,
fontStyle: 'normal',
display: 'swap'
});
在组件里使用的方式如下,这样Next.js会在构建时处理该字体并生成内联预加载,避免路径丢失。
import { myCustomFont } from './fonts/loader';
export default function Home() {
return (
<div className={myCustomFont.className}>
这是使用本地字体的文本
</div>
);
}
这种写法的好处是字体作为模块依赖被明确引用,Vercel构建器能追踪到它。缺点是如果字体格式不被支持(如旧版ttf未转woff2),仍可能失败,因此建议统一用woff2。
相较于把字体丢进public再用CSS的@font-face手动引,next/font方案能配合Vercel的优化管线,减少布局偏移,也降低路径出错概率。
三、当标准写法仍失败时怎么办
若你确认用了标准写法却依旧解析失败,多半是目录结构或配置文件拦截了字体。此时可在next.config.js里用webpack补充拷贝逻辑,强制把字体带进包。
// next.config.js
module.exports = {
webpack: (config) => {
config.module.rules.push({
test: /.(woff2|woff|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'static/media/[name][ext]'
}
});
return config;
}
};
这段配置告诉webpack把所有字体类文件当成资源输出到固定位置,绕过某些默认忽略规则。需注意,若同时用next/font/local,应避免重复处理导致哈希冲突。
另外检查vercel.json是否存在files字段限制了包含范围。若有,请把字体目录加进去,例如:
{
"files": [
"app",
"public",
"fonts"
]
}
如此一来,Vercel在部署前就会把fonts目录纳入函数与静态资源同步,不再出现线上无字体文件的情况。
四、验证与总结
修复后,本地执行next build并搜索输出目录确认字体存在,再推送到Vercel。部署完成后用浏览器开发者工具查看网络请求,字体文件状态应为200且格式正确。
整体来看,Next.js本地字体在Vercel解析失败并不是框架缺陷,而是资源追踪与忽略规则叠加的结果。抓住依赖声明、目录可见、构建配置三点,就能稳定解决。后续新增字体也应沿用同一目录与引入方式,避免再次踩坑。