在Next.js中“use client”指令如何工作13

gzjq41n4  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(596)

我正在尝试使用Next.js 13.4和新的app/目录来进行服务器端渲染。根据我的理解,每个组件都是一个 * 服务器 * 组件(即呈现的服务器端)。
但是,我们可以使用'use client'指令强制将组件转换为 client 组件。
下面,我设置了一个简单的“Hello World”组件,首先作为服务器组件,然后作为客户端组件。在每种情况下,我都比较了页面源代码。

src/app/page.js(服务器组件)

export default function Home() {
  return (
    <main>
      <h1>Hello World</h1>
    </main>
  )
}

Chrome >查看页面源

<!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}\n"])</script>
  <script>self.__next_f.push([1, "1:[[],[\"$\",\"$L2\",null,{\"assetPrefix\":\"\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialHead\":[\"$L3\",null],\"globalErrorComponent\":\"$4\",\"notFound\":[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$L5\",\"$undefined\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid 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\":\"This page could not be found.\"}]}]]}]}]]]}]}],\"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\":[[\"$\",\"main\",null,{\"children\":[\"$\",\"h1\",null,{\"children\":\"Hello World\"}]}],null],\"segment\":\"__PAGE__\"},\"styles\":[]}]}]}],null]}]]\n"])</script>
  <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\":\"viewport\",\"content\":\"width=device-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\":\"Create Next App\"}],[\"$\",\"meta\",null,{\"name\":\"description\",\"content\":\"Generated by create next app\"}],null,null,null,null,n"])</script>
  <script>self.__next_f.push([1, "ull,null,null,null,[\"$\",\"meta\",null,{\"name\":\"viewport\",\"content\":\"width=device-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]\n"])</script>
</body>

</html>

src/app/page.js(客户端组件)

'use client';

export default function Home() {
  return (
    <main>
      <h1>Hello World</h1>
    </main>
  )
}

Chrome >查看页面源

<!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>
  <script>self.__next_f.push([1, "1:[[],[\"$\",\"$L2\",null,{\"assetPrefix\":\"\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialHead\":[\"$L3\",null],\"globalErrorComponent\":\"$4\",\"notFound\":[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$L5\",\"$undefined\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid 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\":\"This page could not be found.\"}]}]]}]}]]]}]}],\"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>
  <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\":\"viewport\",\"content\":\"width=device-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\":\"Create Next App\"}],[\"$\",\"meta\",null,{\"name\":\"description\",\"content\":\"Generated by create next app\"}],null,null,null,null,n"])</script>
  <script>self.__next_f.push([1, "ull,null,null,null,[\"$\",\"meta\",null,{\"name\":\"viewport\",\"content\":\"width=device-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]\n"])</script>
</body>

</html>

我很困惑,因为看起来好像客户端组件已经预先呈现了HTML,但“Hello World”显然存在于源代码中。我期望看到类似于React的根DOM节点的东西--基本上是一个空的div,等待注入一些HTML。我错过了什么?

jgovgodb

jgovgodb1#

From here

客户端组件

客户端组件使您能够向应用程序添加客户端交互性。在Next.js中,它们在服务器上预先呈现,并在客户端上进行水合。您可以将客户端组件视为Pages Router中的组件一直以来的工作方式。

相关问题