Next.js生产环境调用fs.readdirSync出现ENOENT错误求助
Next.js App Router部署Vercel后fs.readdirSync部分页面报错问题
问题情况
使用Next.js App Router,通过fs.readdirSync()读取data/posts/目录下的markdown文章文件,本地运行完全正常,但部署到Vercel后出现异常:将读取逻辑封装为FetchPosts函数并在两个页面调用时,其中一个页面正常工作,另一个页面抛出Error: ENOENT: no such file or directory, scandir 'data/posts/'错误。构建及部署过程无报错,仅访问网站时触发该错误。
相关代码
FetchPosts函数
const fs = require('fs'); export async function FetchPosts() { // read all markdown files in this dir // get an array containing filenames let posts = fs.readdirSync('data/posts/'); try { let data = []; let promises = []; posts.forEach((filename) => { let promise = new Promise((resolve, reject) => { // use async so i can import module dynamically (async () => { // i used a webpack plugin called frontmatter-markdown-loader // this allows me to import markdown files as a module await import(`data/posts/${filename}`) .then((post) => { // substring is to remove `.md` in the filename const slug = filename.substring(filename.indexOf('.'), 0) resolve({ "slug": slug, "attributes": post.attributes, "html": post.html }) } ) })(); }) promises.push(promise); }) data = await Promise.all(promises); // sort posts by date data.sort((a, b) => { let dateA = new Date(a.attributes.date); let dateB = new Date(b.attributes.date); return dateB - dateA; }) return data } catch(err) { throw err } }
正常工作的页面(Blog)
import { FetchPosts } from '@/lib/fetchPosts' // ... export default async function Blog() { const posts = await FetchPosts(); return ( <> <Header /> <article> <Heading /> <Padding> <BlogCategories /> <BlogList posts={posts} /> </Padding> </article> </> ) }
报错的页面(分类页)
import categories from "@/data/categories.data"; import { FetchPosts } from "@/lib/fetchPosts"; // ... export default async function Page({ params }) { const posts = await FetchPosts(); let data = []; posts?.map((post) => { if (post.attributes.category == params.slug) data.push(post) }) return ( <> <Header /> <article> <Heading /> <Padding> <BlogList posts={data} /> </Padding> </article> </> ) }
原因分析
- 相对路径解析差异:本地开发时Node.js工作目录为项目根目录,但Vercel部署后,不同页面组件的执行上下文工作目录可能不同,导致
fs.readdirSync('data/posts/')无法正确定位目标目录。 - 动态import路径不确定性:未使用Next.js别名的相对路径
data/posts/${filename},在Vercel的静态优化或页面模块解析逻辑中,可能出现路径解析错误。 - 代码潜在逻辑问题:原slug截取代码
filename.substring(filename.indexOf('.'), 0)因参数顺序错误会返回空字符串,虽不是本次报错原因,但会导致后续功能异常。
解决方案
1. 使用绝对路径读取目录
引入path模块,结合process.cwd()生成绝对路径,确保无论执行上下文如何都能正确定位目录:
const fs = require('fs'); const path = require('path'); export async function FetchPosts() { const postsDir = path.resolve(process.cwd(), 'data/posts'); let posts = fs.readdirSync(postsDir); // 后续逻辑保持不变 }
2. 修正动态import路径
使用Next.js别名@确保路径解析一致:
await import(`@/data/posts/${filename}`)
3. 修复slug截取逻辑
调整substring参数顺序,正确截取文件名去除.md后缀:
const slug = filename.substring(0, filename.indexOf('.'));
4. 简化Promise逻辑(可选)
原代码Promise封装冗余,可直接用map生成Promise数组提升可读性:
const promises = posts.map(async (filename) => { const post = await import(`@/data/posts/${filename}`); const slug = filename.substring(0, filename.indexOf('.')); return { slug, attributes: post.attributes, html: post.html }; }); data = await Promise.all(promises);
5. 确认Vercel部署包含目标目录
检查.vercelignore文件,确保未配置忽略data/posts目录(默认Vercel会包含项目根目录下所有文件)。
内容的提问来源于stack exchange,提问作者Eltrac
相关产品推荐
相关产品推荐

