找不到module:无法在 Next.js 应用程序中解析“fs”
如果您使用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
}
}
为了 webpack5
module.exports = {
webpack5: true,
webpack: (config) => {
config.resolve.fallback = { fs: false };
return config;
},
};
注意:对于其他module,例如path
,您可以添加多个参数,例如
{
fs: false,
path: false
}
fs
,path
或其他节点本机module只能在服务器端代码中使用,如“getServerSide”函数。如果您尝试在客户端中使用它,即使您只是使用 console.log 也会出错。该 console.log 也应该在服务器端函数中运行。
当您导入“fs”并在服务器端使用它时,next.js 足够聪明,可以看到您在服务器端使用它,因此它不会将该导入添加到客户端包中
我使用的软件包之一给了我这个错误,我用
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback.fs = false
}
return config
},
}
但这在终端上发出警告:
"Critical dependency: require function is used in a way in which
dependencies cannot be statically extracted"
然后我尝试在浏览器上加载节点module。我从 node_modules 复制了节点module的“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"
/>
</>
)
}
这个包被附加到window
对象和 node_modules 源代码的 index.js 中:
if (typeof window !== "undefined") {
window.TruffleContract = contract;
}
所以我可以访问这个脚本作为window.TruffleContract
. 但这不是一种有效的方式。
可能是您尝试实现的module不应该在浏览器中运行。即它只是服务器端。
我在 NextJS 应用程序中遇到此错误,因为我export
在
export function getStaticProps()
最小的可重复示例
一个干净的最小示例将对 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' } }
}
包.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"
}
}
运行:
npm install
npm run dev
现在让我们添加一个假人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' } }
}
失败:
Module not found: Can't resolve 'fs'
这并不奇怪,因为 Next.js 无法知道那fs
只是服务器,而且我们不希望它只是忽略随机要求错误,对吗?Next.js 只知道getStaticProps
因为这是一个硬编码的 Next.js 函数名称。
好的,让我们通过使用fs
inside通知 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' } }
}
头脑等于吹。所以我们明白,任何提到fs
的主体内部getStaticProps
,即使是像上面那样无用的,都会让 Next.js/Webpack 明白它将是仅限服务器的。
事情对getServerSideProps
和 也一样getStaticPaths
。
高阶组件 (HOC) 必须在它们自己的文件中
现在,我们分解出的方式IndexPage
,并getStaticProps
在不同但类似的网页是用肝卵圆细胞,这是刚刚返回其他函数的函数。
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)
但如果你这样做,你会难过地看到:
Module not found: Can't resolve 'fs'
所以我们理解了另外一件事: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)
页面/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)
前端.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>
</>
}
}
返回.js
// Server-only code.
const fs = require('fs')
export function makeGetStaticProps(isBlue) {
return () => {
return { props: {
fs: Object.keys(fs).join(' '),
isBlue,
} }
}
}
Webpack 必须看到该名称makeGetStaticProps
被分配给getStaticProps
,因此它决定整个 back
文件是仅服务器的。
请注意,如果您尝试将back.js
和合并front.js
到单个文件中,则它不起作用,可能是因为当您执行export default makeIndexPage(true)
webpack时,它必然会尝试将整个front.js
文件拉入前端,其中包括 fs,因此它失败了。
这导致库文件在以下之间自然拆分:
front.js
和front/*
:前端+后端文件。这些对前端来说是安全的。后端可以做前端可以做的任何事情(我们正在做 SSR 对吗?)所以那些也可以从后端使用back.js
和back/*
:仅后端文件。这些只能由后端使用,在前端导入它们会导致错误