【发布时间】:2021-10-27 01:48:41
【问题描述】:
我遇到了一些非常奇怪的行为。我有一个更大的项目,但出于演示目的,我认为以下简单的 Next.JS 应用程序就足够了。
重现步骤:
-
创建一个简单的 Next.JS 项目:
npx create-next-app my-next-app --ts -
将以下2个文件添加到项目中:
// ./componens/logs.ts
export function createLogger(options: { name: string }) {
const logIdObj = { logId: 0 };
function vivify(o: { logId: number }) {
let n = 0;
Object.defineProperty(o, 'logId', {
get: () => {
console.warn(`Getting logId for logger ${options.name}: ${n}`);
return n;
},
set: (v: number) => {
console.warn(`Setting logId for logger ${options.name}: ${v}`);
n = v;
},
});
}
vivify(logIdObj);
return (message: string) => {
logIdObj.logId += 1;
console.log(`${logIdObj.logId}. ${message}`);
};
}
// ./componens/Chat.tsx
import { useEffect } from 'react';
import { createLogger } from './logs';
const log = createLogger({ name: 'Chat' });
export default function Chat(): JSX.Element {
log('render');
useEffect(() => {
log('useEffect');
}, []);
return <div>Some content</div>;
}
- 修改主页文件
./pages/index.tsx如下:
import type { NextPage } from 'next'
import Chat from '../components/Chat'
const Home: NextPage = () => {
return <>
<Chat />
<h1>Welcome</h1>
</>
}
export default Home
-
在开发模式下运行项目:
npm run dev -
打开浏览器,导航到
localhost:3000,看看你会在控制台中看到什么:
正如您在./componens/logs.ts 中看到的,对logIdObj 对象的logId 属性的访问被有意包装在getter-setter 拦截器中以记录对它的任何类型的访问,但根据日志值已更改而无需被访问了。
这是我的环境:
Distributor ID: Debian
Description: Debian GNU/Linux 10 (buster)
Release: 10
Codename: buster
node: v14.17.0
npm: 7.22.0
还有我的package.json:
{
"name": "my-next-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "11.1.2",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@types/react": "17.0.30",
"eslint": "7.32.0",
"eslint-config-next": "11.1.2",
"typescript": "4.4.4"
}
}
我猜它可能与内部 Next.JS 逻辑有某种关系,但无论如何它不应该像这样工作。
一如既往,我们将不胜感激。
更新:
虽然第一次回复(感谢Ben)没有明确回答问题的原因,但它提供了可能出现问题的线索。所以我修改了./componens/logs.ts这样的:
const allLogs = [] as { message: string, logId: number, time: number }[];
if (typeof window !== 'undefined') {
// @ts-ignore
window.allLogs = allLogs;
}
export function createLogger(options: { name: string }) {
let logId = 0;
return (message: string) => {
logId += 1;
allLogs.push({ message, logId, time: Date.now() });
console.log(`${logId}. ${message}`);
};
}
现在我们可以清楚地看到,渲染实际上发生了两次,console.log() 并不总是产生实际的日志。不过,目前尚不清楚原因。
有什么解释吗?
【问题讨论】:
-
你在开发环境吗?您是否启用了
StrictMode? -
抱歉,我将 typescript 的
strict模式与 react 的StrictMode混淆了。实际上,这是一个正确的线索 -
我很高兴它成功了。
标签: javascript reactjs typescript next.js