React Next.js - 关于 SSR 模式的三个相关问题
React Next.js - Three related questions about SSR patterns
下面的问题是关于 next.js 的用法和 react 实现 SSR 构建在彼此之上,所以我想我会把它写成一个 post。我的主要问题是第三个,但我觉得我需要先理解前两个问题才能到达那里。所以我们开始吧:
1.客户端收到后,整个页面总是从头开始重新执行,我说得对吗?
考虑这个 next.js 页面组件:
const Page = () => {
const [state, setState] = useState(getState());
function getState() {
console.log("compute initial state");
return 1;
}
return <>{state}</>;
};
据我所知,getState()
在服务器和客户端上都执行。如果我只想在服务器上执行该计算,我必须通过 getInitialProps()
resp 来完成。 getServersideProps()
,对吧?
2。预渲染文档如果立即被丢弃,对客户有什么价值?
将第一个问题更进一步,如果无论如何都会从头开始重新计算,为什么将预呈现的文档事件交给客户端。客户获得初始文件有什么好处?好的,如果客户端根本无法执行 js,他们至少有一些东西。但仅此而已吗?
3。这是否意味着我不必要地必须在客户端“双重渲染”某些组件?
假设我的部分代码依赖于 window
并且无法在服务器上执行。正如我发现的不同 articles 中所解释的那样,如果我在代码中依赖 typeof window === "undefined"
之类的检查,它可能会导致问题(并做出反应警告)。相反,我认为更好的方法是在第一次渲染后使用 useEffect
:
执行这些功能
const Page = () => {
const [value, setValue] = useState();
// Effect will be executed after the first render, e.g. never on the server
useEffect(() => {
const value = window.innerWidth; // Some computations or subscriptions that depend on window
setValue(value)
}, []);
return (
<>
{!value && <h1>value pending ...</h1>}
{value && <h1>{value}</h1>}
</>
);
};
现在,从 SSR 的角度来看,使用此模式的应用程序很好。但是我要介绍在客户端已经完成的额外工作:即使 window
是在第一次渲染时定义的,它也只能在第二次渲染时使用。
对于这个较小的示例,这并没有起到很大的作用,但在较大的应用程序中,可能会出现闪烁,因为几毫秒后更新了不必要的首次渲染。此外,代码变得更难阅读且更容易出错。
我在这里想要的解决方案与上面的第一个问题有关:我能以某种方式避免应用程序从头开始,而是直接从第二个渲染开始吗?到目前为止,我是否错过了某种模式?
注意:当然我可以重写组件以简单地检查是否定义了 window
:
return (
<>
<h1>{window ? window.innerWidth : "value pending ..."}</h1>
</>
);
然而,这将导致反应警告并导致其他问题,如我在上面喜欢的文章中所述。另见 react docs:
React expects that the rendered content is identical between the server and the client. It can patch up differences in text content, but you should treat mismatches as bugs and fix them.
非常感谢您的帮助!
客户端收到后整个页面总是从头开始重新执行,我说的对吗?
是也不是。最初的html建立在服务器上,并作为html发送给客户端,然后react被水化,让你的页面变得可交互。例如,考虑这个“页面”:
export default function MyPage() {
const [greeting, setGreeting] = React.useState("Hello")
React.useEffect(() => {
setGreeting("Goodbye")
}, [])
return (<p>{greeting}</p>)
}
当页面在服务器上呈现时,它将呈现为 html 并将该 html 发送到客户端。因此,如果您检查页面的源代码,您会看到:
<p>Hello</p>
然后,在客户端上,React 被水化并 useEffect
运行s,所以在浏览器中你会看到一个带有“再见”一词的段落。
无论您使用的是 SSR(html 是在服务器上按需创建的)还是 SSG(html 在构建时创建到静态 html 页)。
预渲染文档如果立即被丢弃,对客户有什么价值?
看到我的第一点,整篇文档都不会丢。 SSR 中的值是您是否希望初始问候语文本不是“你好”。例如,如果您希望服务器解析身份验证令牌、获取用户配置文件并在页面加载时使用“Hello Jim”作为 greeting
的种子,您会更喜欢 SSR。如果在将 html 发送到客户端之前没有任何服务器端处理,您可以选择 SSG。
考虑这两个“页面”:
// Using SSR
export default function MyPage({customerName}) {
return (<p>{customerName}</p>)
}
// Using SSG
export default function MyPage({customerName}) {
const [greeting, setGreeting] = React.useState("Hello")
React.useEffect(() => {
// Call server to get the customer's name
const name = myApi.get('/name')
setGreeting(`Hello ${name}`)
}, [])
return (<p>{greeting}</p>)
}
在第一个示例中,服务器使用客户名称(来自服务器上的某个进程)呈现 p
标记,因此 html 源代码将包含该客户的名称。这里什么都没有扔掉。
在第二个示例中,站点构建为 html,源代码的 p
标记显示“Hello”。访问页面时,useEffect
运行s 和 p
标签会在您的 api 响应时更新。因此用户将看到“Hello”x 微秒,然后它将切换为“Hello Jim”。
这是否意味着我不必在客户端“双重渲染”某些组件?
否 - 您可以控制在服务器和客户端呈现的内容。如果您在服务器上为组件播种数据并且不在客户端更改它,则它不会重新呈现。
在您的示例中,是的 - 您是双重渲染。但你可能不需要。如您所述,window
在客户端上不存在。如果您绝对必须在获得 window 大小之前向您的用户显示您的“值待定...”行,那么您将进行双重呈现 - 一次在服务器上填充该字符串,然后一次替换它当在客户端反应水合物时。
如果你不需要显示挂起的行,只需要在 window
实际存在时在客户端显示值,你可以这样重写:
export default function Page() {
const [value, setValue] = React.useState();
React.useEffect(() => {
const newValue = window.innerWidth; // Some computations or subscriptions that depend on window
setValue(newValue)
}, []);
if(value) {
return <h1>{value}</h1>
}
return null
};
在这种情况下,服务器上不会呈现任何内容,因为服务器上没有 value
。只有当客户端被水合时,才会 useEffect
运行,更新 value
,并渲染你的组件一次。
下面的问题是关于 next.js 的用法和 react 实现 SSR 构建在彼此之上,所以我想我会把它写成一个 post。我的主要问题是第三个,但我觉得我需要先理解前两个问题才能到达那里。所以我们开始吧:
1.客户端收到后,整个页面总是从头开始重新执行,我说得对吗?
考虑这个 next.js 页面组件:
const Page = () => {
const [state, setState] = useState(getState());
function getState() {
console.log("compute initial state");
return 1;
}
return <>{state}</>;
};
据我所知,getState()
在服务器和客户端上都执行。如果我只想在服务器上执行该计算,我必须通过 getInitialProps()
resp 来完成。 getServersideProps()
,对吧?
2。预渲染文档如果立即被丢弃,对客户有什么价值?
将第一个问题更进一步,如果无论如何都会从头开始重新计算,为什么将预呈现的文档事件交给客户端。客户获得初始文件有什么好处?好的,如果客户端根本无法执行 js,他们至少有一些东西。但仅此而已吗?
3。这是否意味着我不必要地必须在客户端“双重渲染”某些组件?
假设我的部分代码依赖于 window
并且无法在服务器上执行。正如我发现的不同 articles 中所解释的那样,如果我在代码中依赖 typeof window === "undefined"
之类的检查,它可能会导致问题(并做出反应警告)。相反,我认为更好的方法是在第一次渲染后使用 useEffect
:
const Page = () => {
const [value, setValue] = useState();
// Effect will be executed after the first render, e.g. never on the server
useEffect(() => {
const value = window.innerWidth; // Some computations or subscriptions that depend on window
setValue(value)
}, []);
return (
<>
{!value && <h1>value pending ...</h1>}
{value && <h1>{value}</h1>}
</>
);
};
现在,从 SSR 的角度来看,使用此模式的应用程序很好。但是我要介绍在客户端已经完成的额外工作:即使 window
是在第一次渲染时定义的,它也只能在第二次渲染时使用。
对于这个较小的示例,这并没有起到很大的作用,但在较大的应用程序中,可能会出现闪烁,因为几毫秒后更新了不必要的首次渲染。此外,代码变得更难阅读且更容易出错。
我在这里想要的解决方案与上面的第一个问题有关:我能以某种方式避免应用程序从头开始,而是直接从第二个渲染开始吗?到目前为止,我是否错过了某种模式?
注意:当然我可以重写组件以简单地检查是否定义了 window
:
return (
<>
<h1>{window ? window.innerWidth : "value pending ..."}</h1>
</>
);
然而,这将导致反应警告并导致其他问题,如我在上面喜欢的文章中所述。另见 react docs:
React expects that the rendered content is identical between the server and the client. It can patch up differences in text content, but you should treat mismatches as bugs and fix them.
非常感谢您的帮助!
客户端收到后整个页面总是从头开始重新执行,我说的对吗?
是也不是。最初的html建立在服务器上,并作为html发送给客户端,然后react被水化,让你的页面变得可交互。例如,考虑这个“页面”:
export default function MyPage() {
const [greeting, setGreeting] = React.useState("Hello")
React.useEffect(() => {
setGreeting("Goodbye")
}, [])
return (<p>{greeting}</p>)
}
当页面在服务器上呈现时,它将呈现为 html 并将该 html 发送到客户端。因此,如果您检查页面的源代码,您会看到:
<p>Hello</p>
然后,在客户端上,React 被水化并 useEffect
运行s,所以在浏览器中你会看到一个带有“再见”一词的段落。
无论您使用的是 SSR(html 是在服务器上按需创建的)还是 SSG(html 在构建时创建到静态 html 页)。
预渲染文档如果立即被丢弃,对客户有什么价值?
看到我的第一点,整篇文档都不会丢。 SSR 中的值是您是否希望初始问候语文本不是“你好”。例如,如果您希望服务器解析身份验证令牌、获取用户配置文件并在页面加载时使用“Hello Jim”作为 greeting
的种子,您会更喜欢 SSR。如果在将 html 发送到客户端之前没有任何服务器端处理,您可以选择 SSG。
考虑这两个“页面”:
// Using SSR
export default function MyPage({customerName}) {
return (<p>{customerName}</p>)
}
// Using SSG
export default function MyPage({customerName}) {
const [greeting, setGreeting] = React.useState("Hello")
React.useEffect(() => {
// Call server to get the customer's name
const name = myApi.get('/name')
setGreeting(`Hello ${name}`)
}, [])
return (<p>{greeting}</p>)
}
在第一个示例中,服务器使用客户名称(来自服务器上的某个进程)呈现 p
标记,因此 html 源代码将包含该客户的名称。这里什么都没有扔掉。
在第二个示例中,站点构建为 html,源代码的 p
标记显示“Hello”。访问页面时,useEffect
运行s 和 p
标签会在您的 api 响应时更新。因此用户将看到“Hello”x 微秒,然后它将切换为“Hello Jim”。
这是否意味着我不必在客户端“双重渲染”某些组件?
否 - 您可以控制在服务器和客户端呈现的内容。如果您在服务器上为组件播种数据并且不在客户端更改它,则它不会重新呈现。
在您的示例中,是的 - 您是双重渲染。但你可能不需要。如您所述,window
在客户端上不存在。如果您绝对必须在获得 window 大小之前向您的用户显示您的“值待定...”行,那么您将进行双重呈现 - 一次在服务器上填充该字符串,然后一次替换它当在客户端反应水合物时。
如果你不需要显示挂起的行,只需要在 window
实际存在时在客户端显示值,你可以这样重写:
export default function Page() {
const [value, setValue] = React.useState();
React.useEffect(() => {
const newValue = window.innerWidth; // Some computations or subscriptions that depend on window
setValue(newValue)
}, []);
if(value) {
return <h1>{value}</h1>
}
return null
};
在这种情况下,服务器上不会呈现任何内容,因为服务器上没有 value
。只有当客户端被水合时,才会 useEffect
运行,更新 value
,并渲染你的组件一次。