结果到达客户端时,Next JS 服务器端渲染如何工作?
How does Next JS server side rendering works when result hits client?
我在 NextJS 应用程序中遇到问题。
在 _app.js
文件中,我实例化了一个 Context
(它又实例化了一个 State
)
在我的页面 ContextTest
我声明了一个 getServerSideProps()
.
returns 来自 API 的一些数据被传递到 _app.js
并保存到 Context's state
。
到目前为止,我已经有了我需要的数据。
然后转到目标页面组件,该组件读取上下文的状态并设置一个变量。
组件成功输出我的变量,形式为
let userName = userState.username ? userState.username : 'Guest'
页面加载时,我检查了请求中的 HTML,确实我有 userState.username
值。
但是浏览器中显示的 HTML 有 Guest
.
有两点需要注意:
1) 服务器端按预期呈现
2)该应用程序在该页面的客户端中再次执行(那么为什么我要在应用程序服务器端呈现?)。结果是这个页面没有 getServerSideProps()
的结果,所以我的状态是空的,我在控制台中收到警告。
3)浏览器显示的HTML是客户端执行React app的结果。 (为什么?)
__app.js
import React, {useContext} from "react";
import {UserContext} from "../lib/Context/UserContext"
export default function RankerApp({Component, pageProps}) {
const [userState, setUserState] = useContext(UserContext)();
const [listState, setListState] = useContext(ListContext)();
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState({...userState, ...pageProps.serverContext.user})
}
delete pageProps['serverContext'];
return (
<ListContext.Provider value={[listState, setListState]}>
<UserContext.Provider value={[userState, setUserState]}>
<Component {...pageProps} />
</UserContext.Provider>
</ListContext.Provider>
);
}
pages/contextTest.js
import React, {useContext} from 'react';
import {UserContext} from "../lib/Context/UserContext";
import Link from "next/link";
import UserContextSeeder from "../lib/User/UserContextSeeder";
export default function ContextPage1(props) {
let [userState, setUserState] = useContext(UserContext);
const currentLoggedInUserData = userState.loggedInUserId ? userState.users[userState.loggedInUserId] : null;
const currentLoggedInUserName = currentLoggedInUserData ? currentLoggedInUserData.userName : 'Guest';
return (
<div>
<h1>Page 1</h1>
<Link href="contextTest2">
<a>Page 2</a>
</Link>
<h1>Current Logged In User: {currentLoggedInUserName}</h1>
</div>
);
}
export async function getServerSideProps(context) {
let userContext = {};
// await UserContextSeeder(context).then(context => {userContext = context}); commented for simplicty
userContext = {loggedInUserId: 222, users: {222: {userName: "Jorge"}}; // suppose this is the result
return {
props: {
serverContext: {
user: {...userContext}
}
},
}
}
更多上下文:https://github.com/zeit/next.js/discussions/11871
让我们考虑一个 NextJS 没有水合的世界。您已经从服务器构建了 DOM 并将其发送到客户端,客户端显示了 HTML,大家都很高兴。现在 React 必须执行一个 useEffect
,这将触发 DOM 中的一些变化,但是如果 React 没有 DOM,比较虚拟 DOM 会是什么样子] 过程?
因此,即使我们已经在服务器上完成了,我们也需要在客户端上进行 hydration/render 处理。服务器端呈现不是我们为浏览器节省一些工作的过程,它是我们确保内容尽早准备好供用户和机器人查看的方式。
Josh Comeau 对 his blog 有很好的解释。
换句话说,让我们看一下这段代码:
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState({...userState, ...pageProps.serverContext.user})
}
delete pageProps['serverContext'];
return ();
这里称为 side effect,正在 render
中执行。虽然这适用于普通 React,但它在 StrictMode 中被禁止并且会因 Suspense 而中断(在正常情况下它也会中断)。所以我们把它移到 useEffect
.
useEffect(() => {
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState(prevUserState => ({...prevUserState, ...pageProps.serverContext.user}));
}
}, [])
请注意,我没有删除页面道具,因为我们不需要。
useEffect
仅在客户端执行,因此其效果将仅在客户端显示。如果你想让它们出现在第一次渲染中,你必须使用 prop 来渲染用户名。示例:
const [userState, setUserState] = useContext(UserContext)(props.serverContext ? props.serverContext.user : null);
在上下文挂钩中:
const initialState = {
loggedInUserId: undefined,
users: {}
}
const userContextUpdater = (initialUser) => {
let [state, setState] = useState({
...initialState,
users: initialUser || initialStateusers,
})
return [state, setState];
}
我在 NextJS 应用程序中遇到问题。
在 _app.js
文件中,我实例化了一个 Context
(它又实例化了一个 State
)
在我的页面 ContextTest
我声明了一个 getServerSideProps()
.
returns 来自 API 的一些数据被传递到 _app.js
并保存到 Context's state
。
到目前为止,我已经有了我需要的数据。
然后转到目标页面组件,该组件读取上下文的状态并设置一个变量。 组件成功输出我的变量,形式为
let userName = userState.username ? userState.username : 'Guest'
页面加载时,我检查了请求中的 HTML,确实我有 userState.username
值。
但是浏览器中显示的 HTML 有 Guest
.
有两点需要注意:
1) 服务器端按预期呈现
2)该应用程序在该页面的客户端中再次执行(那么为什么我要在应用程序服务器端呈现?)。结果是这个页面没有 getServerSideProps()
的结果,所以我的状态是空的,我在控制台中收到警告。
3)浏览器显示的HTML是客户端执行React app的结果。 (为什么?)
__app.js
import React, {useContext} from "react";
import {UserContext} from "../lib/Context/UserContext"
export default function RankerApp({Component, pageProps}) {
const [userState, setUserState] = useContext(UserContext)();
const [listState, setListState] = useContext(ListContext)();
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState({...userState, ...pageProps.serverContext.user})
}
delete pageProps['serverContext'];
return (
<ListContext.Provider value={[listState, setListState]}>
<UserContext.Provider value={[userState, setUserState]}>
<Component {...pageProps} />
</UserContext.Provider>
</ListContext.Provider>
);
}
pages/contextTest.js
import React, {useContext} from 'react';
import {UserContext} from "../lib/Context/UserContext";
import Link from "next/link";
import UserContextSeeder from "../lib/User/UserContextSeeder";
export default function ContextPage1(props) {
let [userState, setUserState] = useContext(UserContext);
const currentLoggedInUserData = userState.loggedInUserId ? userState.users[userState.loggedInUserId] : null;
const currentLoggedInUserName = currentLoggedInUserData ? currentLoggedInUserData.userName : 'Guest';
return (
<div>
<h1>Page 1</h1>
<Link href="contextTest2">
<a>Page 2</a>
</Link>
<h1>Current Logged In User: {currentLoggedInUserName}</h1>
</div>
);
}
export async function getServerSideProps(context) {
let userContext = {};
// await UserContextSeeder(context).then(context => {userContext = context}); commented for simplicty
userContext = {loggedInUserId: 222, users: {222: {userName: "Jorge"}}; // suppose this is the result
return {
props: {
serverContext: {
user: {...userContext}
}
},
}
}
更多上下文:https://github.com/zeit/next.js/discussions/11871
让我们考虑一个 NextJS 没有水合的世界。您已经从服务器构建了 DOM 并将其发送到客户端,客户端显示了 HTML,大家都很高兴。现在 React 必须执行一个 useEffect
,这将触发 DOM 中的一些变化,但是如果 React 没有 DOM,比较虚拟 DOM 会是什么样子] 过程?
因此,即使我们已经在服务器上完成了,我们也需要在客户端上进行 hydration/render 处理。服务器端呈现不是我们为浏览器节省一些工作的过程,它是我们确保内容尽早准备好供用户和机器人查看的方式。
Josh Comeau 对 his blog 有很好的解释。
换句话说,让我们看一下这段代码:
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState({...userState, ...pageProps.serverContext.user})
}
delete pageProps['serverContext'];
return ();
这里称为 side effect,正在 render
中执行。虽然这适用于普通 React,但它在 StrictMode 中被禁止并且会因 Suspense 而中断(在正常情况下它也会中断)。所以我们把它移到 useEffect
.
useEffect(() => {
if (pageProps.serverContext && pageProps.serverContext.user) {
setUserState(prevUserState => ({...prevUserState, ...pageProps.serverContext.user}));
}
}, [])
请注意,我没有删除页面道具,因为我们不需要。
useEffect
仅在客户端执行,因此其效果将仅在客户端显示。如果你想让它们出现在第一次渲染中,你必须使用 prop 来渲染用户名。示例:
const [userState, setUserState] = useContext(UserContext)(props.serverContext ? props.serverContext.user : null);
在上下文挂钩中:
const initialState = {
loggedInUserId: undefined,
users: {}
}
const userContextUpdater = (initialUser) => {
let [state, setState] = useState({
...initialState,
users: initialUser || initialStateusers,
})
return [state, setState];
}