为什么 .env.development 在我的 Next.js 应用程序中不起作用?
Why isn't .env.development working in my Next.js app?
我正在编写 Jest/testing 库测试。
假设我们有一个名为 BenchmarksPage
.
的组件
请查看其return语句的第一行。
import {
Box,
capitalize,
Container,
FormControl,
InputLabel,
MenuItem,
Select,
} from '@material-ui/core';
import { NextPage } from 'next';
import React, { useCallback, useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import RequireScope from 'src/components/authentication/RequireScope';
import BenchmarkTable from 'src/components/benchmark/BenchmarkTable';
import DashboardLayout from 'src/components/dashboard/DashboardLayout';
import Heading from 'src/components/Heading';
import useSettings from 'src/hooks/useSettings';
import gtm from 'src/lib/gtm';
import { useDispatch, useSelector } from 'src/store';
import { getAllRollingTwelveCalcs } from 'src/store/rolling-twelve/rolling-twelve.thunk';
import { Timeframe, timeframeMap } from 'src/types/benchmark';
const BenchmarksPage: NextPage = () => {
const { settings } = useSettings();
const dispatch = useDispatch();
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(
Timeframe.Monthly,
);
const company = useSelector((state) => state.company.current);
useEffect(() => {
gtm.push({ event: 'page_view' });
}, []);
useEffect(() => {
dispatch(getAllRollingTwelveCalcs());
}, [company]);
const handleTimeframeChange = useCallback(
(
event: React.ChangeEvent<{
name?: string;
value: Timeframe;
event: Event | React.SyntheticEvent<Element, Event>;
}>,
) => {
setSelectedTimeframe(event.target.value);
},
[],
);
return (
<RequireScope scopes={['query:benchmark-calcs']}>
<DashboardLayout>
<Helmet>
<title>Benchmarks</title>
</Helmet>
<Container maxWidth={settings.compact ? 'xl' : false}>
<Box
sx={{
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'space-between',
mb: 4,
}}
>
<Heading>Benchmarks</Heading>
<FormControl sx={{ width: 300 }}>
<InputLabel>Timeframe</InputLabel>
<Select
sx={{ background: '#ffffff', maxWidth: 400 }}
value={selectedTimeframe}
label="Timeframe"
onChange={handleTimeframeChange}
>
{[...timeframeMap.keys()].map((timeframe) => (
<MenuItem key={timeframe} value={timeframe}>
{capitalize(timeframe)}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
<BenchmarkTable timeframe={selectedTimeframe} />
</Container>
</DashboardLayout>
</RequireScope>
);
};
export default BenchmarksPage;
注意它的 return 值是用 RequireScope
包裹的
RequireScope
只会在用户通过身份验证时呈现其子项
RequireScope
:
import React, { useEffect, useState } from 'react';
import useAuth from 'src/hooks/useAuth';
export interface RequireScopeProps {
scopes: string[];
}
const RequireScope: React.FC<RequireScopeProps> = React.memo((props) => {
const { children, scopes } = props;
const { isInitialized, isAuthenticated, permissions } = useAuth();
const [isPermitted, setIsPermitted] = useState(false);
useEffect(() => {
if (process.env.NEXT_PUBLIC_IS_LOCAL) {
setIsPermitted(true);
}
}, []);
useEffect(() => {
if (isAuthenticated && isInitialized) {
(async () => {
const hasPermissions = scopes
.map((s) => {
return permissions.includes(s);
})
.filter(Boolean);
if (hasPermissions.length === scopes.length) {
setIsPermitted(true);
}
})();
}
}, [isAuthenticated, isInitialized, scopes, permissions]);
if (isPermitted) {
return <>{children}</>;
}
return null;
});
export default RequireScope;
我们只需要将isPermitted
设为true
(useAuth 使用 JWT 登录用户顺便说一下)
现在,当我使用测试库的 render
方法渲染 BenchmarksPage
时,
它不会在 jsdom 上呈现,因为
'isPermitted'
在 RequireScope
中仍然是错误的。
所以,为了isPermitted = true
我设置了
NEXT_PUBLIC_IS_LOCAL=true
在.env.development
根据 RequireScope 中的第一个 useEffect
,isPermitted
现在应该是 true
。
然而,组件仍然没有渲染,returning
<body></div></body>
这仍然意味着 isPermitted 是错误的。
我尝试了什么:
我也试过用.env.local
我可以向您保证我的设置都是正确的(jest.config,使用 MockProvider 包装 BenchmarksPage 等)
为什么 .env.development 不工作,而使 isPermitted = true
?
根据我的理解,一切在逻辑上都是正确的。
编辑:我试过写
NEXT_PUBLIC_IS_LOCAL=真
也在 .env.test 文件中
编辑 - 答案:
必须设置
import { loadEnvConfig } from '@next/env';
export default async (): Promise<void> => {
loadEnvConfig(process.env.PWD);
};
在 setupEnv.ts 文件中。
然后在 package.json 中添加 globalSetup(因为我不使用 jest.config.js -> eslint 与 tsconfig 一起搞乱了它)
"jest": {
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/src/"
},
"testEnvironment": "jsdom",
"globalSetup": "<rootDir>/test/setupEnv.ts"
},
如果您的环境变量在 运行 开发服务器时有效,但在测试时不起作用,这是因为 Next 在单元测试时没有为 Jest 设置变量。
首先,创建一个 .env.test
,设置仅用于测试的环境变量。
然后,为了在测试环境中设置 envs,您必须将其添加到您的测试入口点:
// jest.config.js
module.exports = {
globalSetup: '<rootDir>/__test__/setupEnv.js'
}
// __test__/setupEnv.js
import { loadEnvConfig } from '@next/env'
export default async () => {
const projectDir = process.cwd()
loadEnvConfig(projectDir)
}
您可以将 __test__/
目录更改为您的测试目录。
请注意 .env.local
在测试期间未加载
我正在编写 Jest/testing 库测试。
假设我们有一个名为 BenchmarksPage
.
请查看其return语句的第一行。
import {
Box,
capitalize,
Container,
FormControl,
InputLabel,
MenuItem,
Select,
} from '@material-ui/core';
import { NextPage } from 'next';
import React, { useCallback, useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import RequireScope from 'src/components/authentication/RequireScope';
import BenchmarkTable from 'src/components/benchmark/BenchmarkTable';
import DashboardLayout from 'src/components/dashboard/DashboardLayout';
import Heading from 'src/components/Heading';
import useSettings from 'src/hooks/useSettings';
import gtm from 'src/lib/gtm';
import { useDispatch, useSelector } from 'src/store';
import { getAllRollingTwelveCalcs } from 'src/store/rolling-twelve/rolling-twelve.thunk';
import { Timeframe, timeframeMap } from 'src/types/benchmark';
const BenchmarksPage: NextPage = () => {
const { settings } = useSettings();
const dispatch = useDispatch();
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(
Timeframe.Monthly,
);
const company = useSelector((state) => state.company.current);
useEffect(() => {
gtm.push({ event: 'page_view' });
}, []);
useEffect(() => {
dispatch(getAllRollingTwelveCalcs());
}, [company]);
const handleTimeframeChange = useCallback(
(
event: React.ChangeEvent<{
name?: string;
value: Timeframe;
event: Event | React.SyntheticEvent<Element, Event>;
}>,
) => {
setSelectedTimeframe(event.target.value);
},
[],
);
return (
<RequireScope scopes={['query:benchmark-calcs']}>
<DashboardLayout>
<Helmet>
<title>Benchmarks</title>
</Helmet>
<Container maxWidth={settings.compact ? 'xl' : false}>
<Box
sx={{
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'space-between',
mb: 4,
}}
>
<Heading>Benchmarks</Heading>
<FormControl sx={{ width: 300 }}>
<InputLabel>Timeframe</InputLabel>
<Select
sx={{ background: '#ffffff', maxWidth: 400 }}
value={selectedTimeframe}
label="Timeframe"
onChange={handleTimeframeChange}
>
{[...timeframeMap.keys()].map((timeframe) => (
<MenuItem key={timeframe} value={timeframe}>
{capitalize(timeframe)}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
<BenchmarkTable timeframe={selectedTimeframe} />
</Container>
</DashboardLayout>
</RequireScope>
);
};
export default BenchmarksPage;
注意它的 return 值是用 RequireScope
RequireScope
只会在用户通过身份验证时呈现其子项
RequireScope
:
import React, { useEffect, useState } from 'react';
import useAuth from 'src/hooks/useAuth';
export interface RequireScopeProps {
scopes: string[];
}
const RequireScope: React.FC<RequireScopeProps> = React.memo((props) => {
const { children, scopes } = props;
const { isInitialized, isAuthenticated, permissions } = useAuth();
const [isPermitted, setIsPermitted] = useState(false);
useEffect(() => {
if (process.env.NEXT_PUBLIC_IS_LOCAL) {
setIsPermitted(true);
}
}, []);
useEffect(() => {
if (isAuthenticated && isInitialized) {
(async () => {
const hasPermissions = scopes
.map((s) => {
return permissions.includes(s);
})
.filter(Boolean);
if (hasPermissions.length === scopes.length) {
setIsPermitted(true);
}
})();
}
}, [isAuthenticated, isInitialized, scopes, permissions]);
if (isPermitted) {
return <>{children}</>;
}
return null;
});
export default RequireScope;
我们只需要将isPermitted
设为true
(useAuth 使用 JWT 登录用户顺便说一下)
现在,当我使用测试库的 render
方法渲染 BenchmarksPage
时,
它不会在 jsdom 上呈现,因为
'isPermitted'
在 RequireScope
中仍然是错误的。
所以,为了isPermitted = true
我设置了
NEXT_PUBLIC_IS_LOCAL=true
在.env.development
根据 RequireScope 中的第一个 useEffect
,isPermitted
现在应该是 true
。
然而,组件仍然没有渲染,returning
<body></div></body>
这仍然意味着 isPermitted 是错误的。
我尝试了什么:
我也试过用.env.local
我可以向您保证我的设置都是正确的(jest.config,使用 MockProvider 包装 BenchmarksPage 等)
为什么 .env.development 不工作,而使 isPermitted = true
?
根据我的理解,一切在逻辑上都是正确的。
编辑:我试过写
NEXT_PUBLIC_IS_LOCAL=真
也在 .env.test 文件中
编辑 - 答案:
必须设置
import { loadEnvConfig } from '@next/env';
export default async (): Promise<void> => {
loadEnvConfig(process.env.PWD);
};
在 setupEnv.ts 文件中。
然后在 package.json 中添加 globalSetup(因为我不使用 jest.config.js -> eslint 与 tsconfig 一起搞乱了它)
"jest": {
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/src/"
},
"testEnvironment": "jsdom",
"globalSetup": "<rootDir>/test/setupEnv.ts"
},
如果您的环境变量在 运行 开发服务器时有效,但在测试时不起作用,这是因为 Next 在单元测试时没有为 Jest 设置变量。
首先,创建一个 .env.test
,设置仅用于测试的环境变量。
然后,为了在测试环境中设置 envs,您必须将其添加到您的测试入口点:
// jest.config.js
module.exports = {
globalSetup: '<rootDir>/__test__/setupEnv.js'
}
// __test__/setupEnv.js
import { loadEnvConfig } from '@next/env'
export default async () => {
const projectDir = process.cwd()
loadEnvConfig(projectDir)
}
您可以将 __test__/
目录更改为您的测试目录。
请注意 .env.local
在测试期间未加载