找不到模块:无法在 Next.js 应用程序中解析“fs”

Iba*_*ikh 3 fs node.js reactjs server-side-rendering next.js

无法确定我的 next.js 应用程序中发生了什么。因为fs是 nodejs 的默认文件系统模块。它给出了找不到模块的错误。

在此处输入图片说明

在此处输入图片说明

dia*_*zlp 53

我花了几个小时在这上面,解决方案也在 Stackoverflow 上,但在不同的问题上 - > /sf/answers/4723505741/

特此,我请求 MOD 许可转发此问题,因为这个问题是第一个出现在 Google 上的问题,并且可能越来越多的人会偶然发现与我相同的问题,所以我会尽力为他们省去一些麻烦

Soo,你需要将其添加到你的next.config.js

module.exports = {

  // Can be safely removed in newer versions of Next.js
  future: {

    // by default, if you customize webpack config, they switch back to version 4.
    // Looks like backward compatibility approach.
    webpack5: true,   
  },

  webpack(config) {
    config.resolve.fallback = {

      // if you miss it, all the other options in fallback, specified
      // by next.js will be dropped.
      ...config.resolve.fallback,  

      fs: false, // the solution
    };
    
    return config;
  },
};
Run Code Online (Sandbox Code Playgroud)

它对我来说就像一种魅力。

编辑:对于较新版本的 Next.JS,该选项future.webpack5已替换为webpack5,默认为true。由于我们在此解决方案中使用 Webpack 5,因此可以安全地删除此选项。 https://nextjs.org/docs/messages/future-webpack5-moved-to-webpack5#possible-ways-to-fix-it


Cir*_*四事件 42

最小可重复示例

一个干净的最小示例将对 Webpack 初学者有益,因为基于使用情况的自动拆分是如此令人兴奋的魔法。

工作你好世界基线:

页面/index.js

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

export function getStaticProps() {
  return { props: { msg: 'hello world' } }
}
Run Code Online (Sandbox Code Playgroud)

包.json

{
  "name": "test",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "12.0.7",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  }
}
Run Code Online (Sandbox Code Playgroud)

运行:

npm install
npm run dev
Run Code Online (Sandbox Code Playgroud)

现在让我们添加一个虚拟对象require('fs')来炸毁东西:

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

const fs = require('fs')

export function getStaticProps() {
  return { props: { msg: 'hello world' } }
}
Run Code Online (Sandbox Code Playgroud)

失败并显示:

Module not found: Can't resolve 'fs' 
Run Code Online (Sandbox Code Playgroud)

这并不奇怪,因为 Next.js 无法知道那fs只是服务器,而且我们也不希望它只是忽略随机的 require 错误,对吧?Next.js 只知道 forgetStaticProps因为这是硬编码的 Next.js 函数名称。

好的,让我们通过fsinside通知 Next.js getStaticProps,以下内容再次起作用:

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

const fs = require('fs')

export function getStaticProps() {
  fs
  return { props: { msg: 'hello world' } }
}
Run Code Online (Sandbox Code Playgroud)

心智等于崩溃。因此我们知道,任何对fs主体内部的提及getStaticProps,即使是像上面这样无用的内容,也会让 Next.js/Webpack 明白它将仅是服务器的。

getServerSideProps对于和 ,事情是一样的getStaticPaths

高阶组件 (HOC) 必须位于自己的文件中

IndexPage现在,我们在不同但相似的页面中进行分解的方法getStaticProps是使用 HOC,它们只是返回其他函数的函数。

HOC 通常会放在多个位置之外pages/,然后从多个位置需要,但是当您要将事物分解出来进行概括时,您可能会想将它们pages/暂时直接放在文件中,例如:

// Client + server code.

import Link from 'next/link'

export function makeIndexPage(isIndex) {
  return (props) => {
    return <>
      <Link href={isIndex ? '/index' : '/notindex'}>
        <a>{isIndex ? 'index' : 'notindex'}</a>
      </Link>
      <div>{props.fs}</div>
      <div>{props.isBlue}</div>
    </>
  }
}

export default makeIndexPage(true)

// Server-only code.

const fs = require('fs')

export function makeGetStaticProps(isBlue) {
  return () => {
    return { props: {
      fs: Object.keys(fs).join(' '),
      isBlue,
    } }
  }
}

export const getStaticProps = makeGetStaticProps(true)
Run Code Online (Sandbox Code Playgroud)

但如果你这样做,你会悲伤地看到:

Module not found: Can't resolve 'fs' 
Run Code Online (Sandbox Code Playgroud)

所以我们明白了另一件事:fs用法必须直接在getStaticProps函数体内,Webpack 无法在子函数中捕获它。

解决这个问题的唯一方法是为仅后端的内容创建一个单独的文件,如下所示:

页面/index.js

// Client + server code.

import { makeIndexPage } from "../front"

export default makeIndexPage(true)

// Server-only code.

import { makeGetStaticProps } from "../back"

export const getStaticProps = makeGetStaticProps(true)
Run Code Online (Sandbox Code Playgroud)

页面/notindex.js

// Client + server code.

import { makeIndexPage } from "../front"

export default makeIndexPage(false)

// Server-only code.

import { makeGetStaticProps } from "../back"

export const getStaticProps = makeGetStaticProps(false)
Run Code Online (Sandbox Code Playgroud)

前端.js

// Client + server code.

import Link from 'next/link'

export function makeIndexPage(isIndex) {
  return (props) => {
    console.error('page');
    return <>
      <Link href={isIndex ? '/notindex' : '/'}>
        <a>{isIndex ? 'notindex' : 'index'}</a>
      </Link>
      <div>{props.fs}</div>
      <div>{props.isBlue}</div>
    </>
  }
}
Run Code Online (Sandbox Code Playgroud)

返回.js

// Server-only code.

const fs = require('fs')

export function makeGetStaticProps(isBlue) {
  return () => {
    return { props: {
      fs: Object.keys(fs).join(' '),
      isBlue,
    } }
  }
}
Run Code Online (Sandbox Code Playgroud)

Webpack 必须看到该名称makeGetStaticProps被分配给getStaticProps,因此它决定整个 back文件仅供服务器使用。

back.js请注意,如果您尝试将和合并front.js到单个文件中,它不起作用,可能是因为当您执行export default makeIndexPage(true)webpack 时,必须尝试将整个front.js文件拉入前端,其中包括 fs,因此它会失败。

这导致库文件在以下之间自然地(基本上几乎是强制性的)分割:

  • front.jsfront/*:前端+后端文件。这些对于前端来说是安全的。后端可以做前端可以做的任何事情(我们正在做 SSR,对吧?),所以这些也可以从后端使用。

    也许这就是许多官方示例中传统“组件”文件夹背后的想法。但这是一个坏名字,因为该文件夹不仅应该包含组件,还应该包含将从前端使用的任何库非组件帮助器/常量。

  • back.jsback/*(或者 ) 之外的任何内容front/*:仅后端文件。这些只能由后端使用,在前端导入它们会导致错误


Zac*_*zer 19

虽然此错误比您遇到的大多数错误需要更多的推理,但它发生的原因很简单。

为什么会发生这种情况

与许多框架不同,Next.js 允许您将仅服务器(不能在浏览器中工作的 Node.js API)代码导入到页面文件中。当 Next.js 构建您的项目时,它会通过检查以下任一内置方法(代码分割)中存在哪些代码,从客户端包中删除仅服务器代码:

  • getServerSideProps
  • getStaticProps
  • getStaticPaths

旁注:有一个演示应用程序可以直观地展示其工作原理。

当您尝试在这些方法之外使用仅服务器代码时,Module not found: can't resolve 'xyz'就会发生此错误。

错误示例 1 - 基本

要重现此错误,让我们从一个简单的 Next.js 页面文件开始

工作文件

/** THIS FILE WORKS FINE! */

import type { GetServerSideProps } from "next";

import fs from "fs"; // our server-only import

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  const fileExists = fs.existsSync("/some-file"); 

  return {
    props: {
      doesFileExist: fileExists,
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};

export default ExamplePage;
Run Code Online (Sandbox Code Playgroud)

现在,让我们通过fs.existsSync方法移到. 区别很微妙,但下面的代码会抛出我们可怕的错误。getServerSidePropsModule not found

错误文件

import type { GetServerSideProps } from "next";
import fs from "fs";

type Props = {
  doesFileExist: boolean;
};

/** ERROR!! - Module not found: can't resolve 'fs' */
const fileExists = fs.existsSync("/some-file");

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: fileExists,
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};

export default ExamplePage;
Run Code Online (Sandbox Code Playgroud)

错误示例 2 - 现实

当您使用包含多种类型代码(客户端+服务器端)的模块时,最常见(且令人困惑)会发生此错误。

假设我有以下模块file-utils.ts

import fs from 'fs'

// This code only works server-side
export function getFileExistence(filepath: string) {
  return fs.existsSync(filepath)
}

// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
  return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
Run Code Online (Sandbox Code Playgroud)

在本模块中,我们有一种仅服务器方法和一种“共享”方法,理论上应该在客户端工作(但正如我们将看到的,理论并不完美)。

现在,让我们尝试将其合并到我们的 Next.js 页面文件中。

/** ERROR!! */

import type { GetServerSideProps } from "next";

import { getFileExistence, formatResult } from './file-utils.ts'

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: getFileExistence('/some-file')
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {

  // ERROR!!!
  return <div>File exists?: {formatResult(doesFileExist)}</div>;
};

export default ExamplePage;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们在这里收到错误,因为当我们尝试使用formatResult客户端时,我们的模块仍然必须导入服务器端代码

为了解决这个问题,我们需要将模块分为两类:

  1. 仅服务器
  2. 共享代码(客户端或服务器)
// file-utils.ts

import fs from 'fs'

// This code (and entire file) only works server-side
export function getFileExistence(filepath: string) {
  return fs.existsSync(filepath)
}
Run Code Online (Sandbox Code Playgroud)
// file-format-utils.ts

// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
  return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以创建一个工作页面文件:

/** WORKING! */

import type { GetServerSideProps } from "next";

import { getFileExistence } from './file-utils.ts' // server only
import { formatResult } from './file-format-utils.ts' // shared

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: getFileExistence('/some-file')
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {formatResult(doesFileExist)}</div>;
};

export default ExamplePage;
Run Code Online (Sandbox Code Playgroud)

解决方案

有两种方法可以解决这个问题:

  1. “正确”的方式
  2. “让它发挥作用”的方式

“正确”的方式

解决此错误的最佳方法是确保您了解发生这种情况的原因(如上所述),并确保您仅在getStaticPathsgetStaticProps、 或getServerSideProps以及 NOWHERE else中使用服务器端代码。

请记住,如果导入包含服务器端和客户端代码的模块,则无法使用该模块客户端的任何导入(请重新访问上面的示例#2)。

“让它发挥作用”的方式

正如其他人所建议的,您可以更改next.config.js为在构建时忽略某些模块。这意味着当 Next.js 尝试在仅服务器代码共享代码之间拆分页面文件时,它不会尝试填充无法构建客户端的 Node.js API。

在这种情况下,您只需要:

/** THIS FILE WORKS FINE! */

import type { GetServerSideProps } from "next";

import fs from "fs"; // our server-only import

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  const fileExists = fs.existsSync("/some-file"); 

  return {
    props: {
      doesFileExist: fileExists,
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};

export default ExamplePage;
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点

如 Webpack 文档的resolve.fallback部分所示,此配置选项的主要原因是因为从 Webpack 开始v5.x,核心 Node.js 模块默认情况下不再是多填充的。因此,此选项的主要目的是为您提供一种方法来定义要使用的 polyfill

当您false作为选项传递时,这意味着“不包含填充”。

虽然这种方法有效,但它可能很脆弱,并且需要持续维护才能包含您引入到项目中的任何新模块。除非您要转换现有项目/支持遗留代码,否则最好选择上面的选项#1,因为它根据 Next.js 实际如何拆分代码来促进更好的模块组织。


Yil*_*maz 15

fspath其他节点本机模块只能在服务器端代码内使用,例如“getServerSide”函数。如果您尝试在客户端中使用它,即使您只是使用 console.log 也会出现错误。console.log 也应该在服务器端函数内运行。

当您导入“fs”并在服务器端使用它时,next.js 足够聪明,可以看到您在服务器端使用它,因此它不会将导入添加到客户端捆绑包中

我使用的一个软件包给了我这个错误,我修复了这个问题

module.exports = {
 
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback.fs = false
    }

    return config
  },
  
}
Run Code Online (Sandbox Code Playgroud)

但这在终端上发出警告:

"Critical dependency: require function is used in a way in which

 dependencies cannot be statically extracted"
Run Code Online (Sandbox Code Playgroud)

然后我尝试在浏览器上加载节点模块。我从node_modules复制了节点模块的“min.js”并放置在“public/js/myPackage.js”中并使用脚本加载它

export default function BaseLayout({children}) {
  return (
    <>
      <Script
        // this in public folder
        src="/js/myPackage.js"
        // this means this script will be loaded first
        strategy="beforeInteractive"
      />
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)

该包附加到window对象和node_modules源代码的index.js中:

if (typeof window !== "undefined") {
  window.TruffleContract = contract;
}
Run Code Online (Sandbox Code Playgroud)

所以我可以以window.TruffleContract. 但这不是一个有效的方法。


Arj*_*ava 5

如果您使用fs,请确保它仅在getInitialProps或内serverSideProps。(任何包括服务器端渲染)。

您可能还需要创建一个next.config.js包含以下内容的文件来构建客户端包:

为了 webpack4

module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = {
        fs: 'empty'
      }
    }

    return config
  }
}
Run Code Online (Sandbox Code Playgroud)

为了 webpack5

module.exports = {
  webpack5: true,
  webpack: (config) => {
    config.resolve.fallback = { fs: false };

    return config;
  },
};
Run Code Online (Sandbox Code Playgroud)

  • 黄金答案!只是我还必须在“fs: false”之后添加“path: false”。 (3认同)