<p>我正在尝试使用 Next.js 13.4 和新的 <code>app/</code> 目录来了解服务器端渲染。据我了解,默认情况下每个组件都是一个<em>服务器</em>组件(即在服务器端呈现)。</p>
<p>但是,我们可以使用 <code>'use client'</code> 指令来强制将组件变成<em>客户端</em>组件。</p>
<p>下面,我设置了一个简单的“Hello World”组件,首先作为服务器组件,然后作为客户端组件。在每种情况下,我都会比较页面源代码。</p>
src/app/page.js
(服务器组件)
;
<pre class="brush:php;toolbar:false;">导出默认函数 Home() {
返回 (
<主要>
你好世界
</主要>
)
}</pre>
<p>Chrome> 查看来源页面代码</p>
;
<html lang="zh-cn">
<头>
创建下一个应用
<元名称=“视口”内容=“宽度=设备宽度,初始比例= 1”/> />
;
</头>
<正文>
<主要>
你好世界
</主要>
;
;
;
<脚本>self.__next_f.push([1, "0:"$L1"n"])</脚本>
<script>self.__next_f.push([1, "2:I{"id":"(app-client)/./node_modules/next/dist/client/components/app-router.js","块":["webpack:static/chunks/webpack.js"],"name":"","async":false}n4:I{"id":"(app-client)/./node_modules/next/ dist/client/components/error-boundary.js","chunks":["webpack:static/chunks/webpack.js"],"name":"","async":false}n6:I{"id ":"(app-client)/./node_modules/next/dist/client/components/layout-router.js","chunks":["app-client-internals:static/chunks/app-client-internals. js"],"name":"","async":false}n7:I{"id":"(app-client)/"]);
<script>self.__next_f.push([1, "./node_modules/next/dist/client/components/render-from-template-context.js","chunks":["app-client-internals:static /chunks/app-client-internals.js"],"名称":"","async":false}n"])脚本>
<script>self.__next_f.push([1, "1:[[],["$","$L2",null,{"assetPrefix":"","initialCanonicalUrl":"/","initialTree ":["",{"children":["__PAGE__",{}]},"$未定义","$未定义",true],"initialHead":["$L3",null],"globalErrorComponent" :"$4","notFound":["$","html",null,{"lang":"en","children":["$","body",null,{"children":[ "$L5","$undefined",[["$","title",null,{"children":"404: 找不到此页面。"}],["$","div", null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color 表情符号\",\"Segoe UI 表情符号\"" ,"height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"}, "children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;背景:#fff;边距:0}.next-error-h1{border-right:1px 实心 rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{颜色: #fff;背景:#000}.next-error-h1{border-right:1px 实心 rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className" :"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize": 24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style" :{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight": "49px","margin":0},"children":"找不到此页面。"}]}]]}]}]]]}]}],"asNotFound":false,"children":[["$","html",null,{"lang":"en","children":["$","body",null,{"孩子”:[“$”,“$L6”,null,{“parallelRouterKey”:“孩子”,“segmentPath”:[“孩子”],“错误”:“$未定义”,“errorStyles”:“$未定义","loading":"$undefined","loadingStyles":"$undefined","hasLoading":false,"template":["$","$L7",null,{}],"templateStyles": “$未定义”,“notFound”:“$未定义”,“notFoundStyles”:“$未定义”,“childProp”:{“当前”:[[“$”,“main”,null,{“children”:[ "$","h1",null,{"children":"Hello World"}]}],null],"segment":"__PAGE__"},"styles":[]}]}]}],null ]}]]n"])脚本>
<script>self.__next_f.push([1, "5:[[["$","meta",null,{"charSet":"utf-8"}],null,null,null,null, null,null,null,null,null,null,["$","meta",null,{"name":"视口","content":"width=设备宽度,initial-scale=1"} ],null,null,null,null,null,null,null,null,null,null,[]],[null,null,null,null],null,null,[null,null,null,null,null ],null,null,null,null,null]n3:[[["$","meta",null,{"charSet":"utf-8"}],["$","title",null ,{"children":"创建下一个应用程序"}],["$","meta",null,{"name":"description","content":"由创建下一个应用程序生成"}],null, null,null,null,n"]);
;
</正文>
</html></pre>
src/app/page.js
(客户端组件)
'使用客户端';
导出默认函数 Home() {
返回 (
<主要>
你好世界
</主要>
)
}</pre>
<p>Chrome > 查看页面源代码</p>
<pre class="brush:php;toolbar:false;"><!DOCTYPE html>
<html lang="en">
<head>
<meta charSet="utf-8" />
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="/_next/static/chunks/polyfills.js" noModule=""></script>
</head>
<body>
<main>
<h1>Hello World</h1>
</main>
<script src="/_next/static/chunks/webpack.js" async=""></script>
<script src="/_next/static/chunks/main-app.js" async=""></script>
<script>(self.__next_f = self.__next_f || []).push([0])</script>
<script>self.__next_f.push([1, "0:"$L1"n"])</script>
<script>self.__next_f.push([1, "2:I{"id":"(app-client)/./node_modules/next/dist/client/components/app-router.js","chunks":["webpack:static/chunks/webpack.js"],"name":"","async":false}n4:I{"id":"(app-client)/./node_modules/next/dist/client/components/error-boundary.js","chunks":["webpack:static/chunks/webpack.js"],"name":"","async":false}n6:I{"id":"(app-client)/./node_modules/next/dist/client/components/layout-router.js","chunks":["app-client-internals:static/chunks/app-client-internals.js"],"name":"","async":false}n7:I{"id":"(app-client)/"])</script>
<script>self.__next_f.push([1, "./node_modules/next/dist/client/components/render-from-template-context.js","chunks":["app-client-internals:static/chunks/app-client-internals.js"],"name":"","async":false}n8:I{"id":"(app-client)/./src/app/page.js","chunks":["app/page:static/chunks/app/page.js"],"name":"","async":false}n"])脚本>
<script>self.__next_f.push([1, "1:[[],["$","$L2",null,{"assetPrefix":"","initialCanonicalUrl":"/","initialTree ":["",{"children":["__PAGE__",{}]},"$未定义","$未定义",true],"initialHead":["$L3",null],"globalErrorComponent" :"$4","notFound":["$","html",null,{"lang":"en","children":["$","body",null,{"children":[ "$L5","$undefined",[["$","title",null,{"children":"404: 找不到此页面。"}],["$","div", null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color 表情符号\",\"Segoe UI 表情符号\"" ,"height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"}, "children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;背景:#fff;边距:0}.next-error-h1{border-right:1px 实心 rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{颜色: #fff;背景:#000}.next-error-h1{border-right:1px 实心 rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className" :"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize": 24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style" :{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight": "49px","margin":0},"children":"找不到此页面。"}]}]]}]}]]]}]}],"asNotFound":false,"children":[["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","loading":"$undefined","loadingStyles":"$undefined","hasLoading":false,"template":["$","$L7",null,{}],"templateStyles":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","childProp":{"current":[["$","$L8",null,{"params":{},"searchParams":{}}],null],"segment":"__PAGE__"},"styles":[]}]}]}],null]}]]n"]);
<script>self.__next_f.push([1, "5:[[["$","meta",null,{"charSet":"utf-8"}],null,null,null,null, null,null,null,null,null,null,["$","meta",null,{"name":"视口","content":"width=设备宽度,initial-scale=1"} ],null,null,null,null,null,null,null,null,null,null,[]],[null,null,null,null],null,null,[null,null,null,null,null ],null,null,null,null,null]n3:[[["$","meta",null,{"charSet":"utf-8"}],["$","title",null ,{"children":"创建下一个应用程序"}],["$","meta",null,{"name":"description","content":"由创建下一个应用程序生成"}],null, null,null,null,n"]);
;
</正文>
</html></pre>
<小时/>>
<p>我很困惑,因为客户端组件似乎已经预渲染了 HTML,但“Hello World”显然存在于源代码中。我希望看到一个 React 根 DOM 节点的东西 - 基本上是一个空的div 等待注入一些 HTML。我这里缺什么?</p>
从这里
服务器和客户端组件在静态渲染期间的渲染方式不同:
现在,通过服务器和客户端组件,React 可以在客户端和服务器上进行渲染,这意味着您可以可以在组件级别选择渲染环境。
默认情况下,
app
路由器使用服务器组件,允许您轻松地在服务器上渲染组件并减少发送到客户端的 JavaScript 量。