Next.js 13 服务器和客户端组件混淆

utk*_*hin 7 reactjs next.js react-server-components next.js13

我有一个服务器组件InitView

const InitView = () => {
    useEffect(() => { });
    return (
        <>
            <Hero/>
            <span className="text-xl font-normal text-gray-100">Now, how do you want to play?</span>
            <GameModeMenu/>
        </>
    );
}

export default InitView;
Run Code Online (Sandbox Code Playgroud)

我还有一个服务器组件View

interface ViewProps {
    children?: React.ReactNode;
}

const View = ({children}:ViewProps) => {
    return (
        <main className="home w-screen h-screen flex flex-col gap-10 justify-start items-center bg-neutral-900 px-8 py-10">
            {children}
        </main>
    );
}

export default View;
Run Code Online (Sandbox Code Playgroud)

这是我的page.tsx

export default function Page() {
  return (
    <View>
        <InitView/>
    </View>
  )
}
Run Code Online (Sandbox Code Playgroud)

当我尝试使用pass-child方法将InitView导入View组件时它会抛出错误;

您正在导入需要 useEffect 的组件。它仅在客户端组件中工作,但其父组件都没有标记为“使用客户端”,因此默认情况下它们是服务器组件。

我完全同意这个错误,因为我正在尝试在服务器组件内使用效果。然而,如果我将代码更改为这样,事情就是这样;

页面.tsx

export default function Page() {
  return (
    <View/>
  )
}
Run Code Online (Sandbox Code Playgroud)

查看.tsx

"use client";

const View = () => {
    return (
        <main className="home w-screen h-screen flex flex-col gap-10 justify-start items-center bg-neutral-900 px-8 py-10">
            <InitView/>
        </main>
    );
}

export default View;
Run Code Online (Sandbox Code Playgroud)

现在错误消失了。澄清;

我可以在 InitView 组件中使用效果,而无需任何“使用客户端”标记,因为我直接将其导入到View(标记为客户端)组件中。

我假设客户端组件中的每个直接导入的(服务器或客户端)组件都将是客户端组件,因为前面的错误表明其父组件都没有标记为“使用客户端”,因此默认情况下它们是服务器组件。

你们有什么想法吗?我是错还是对?

PS 文档说我无法将服务器组件导入客户端组件内,但可以清楚地看到,我可以。我很困惑。

Vic*_*ban 9

这个问题已经有足够的解释了,但我会尝试澄清一些事情。

TL:DR:您需要该'use client'指令才能使用仅限客户端的功能。一旦组件成为客户端,它的嵌套组件也是客户端。无法更改或拦截此行为。

需要澄清的一些重要细节:

  1. 您只能在服务器端组件中使用服务器端内容。例如,直接数据库查询。
  2. 您只能在客户端组件中使用客户端内容。例如,React hooks(useEffect等等useRouter)。
  3. Nextjs 13 默认使用服务器组件,我个人认为这是错误的,会给大多数项目带来额外的麻烦和混乱。
  4. 子组件继承执行的“一侧”。例如,对于您的 3 个组件层次结构page.jsx=> PageMain.jsx=>SomeModal.jsx默认情况下,所有 3 个组件都是服务器端的。
  5. 如果您需要组件内的一些客户端内容PageMain.jsxuseEffectuseStateuseRouter、页面转换、客户端库...),您需要将该组件标记为 client: 'use client'。现在PageMain.jsx将在客户端渲染。它的子项也将在客户端渲染。它的孙子也会这么做。你说对了)))
  6. 一旦某个组件成为客户端组件,就无法再使其任何子组件成为仅服务器组件。
  7. 没有办法打破规则#6。您可能遇到过以下指令:'server-only''client-only'。这些不允许您再次制作仅服务器组件。这些只是为了产生错误。请参阅此处的文档
  8. 从技术上讲,客户端组件也在服务器端预渲染,您可以尝试 Nextjs 12 - 该版本更加简单。“服务器端”、“仅服务器”组件仅在您实际上不需要客户端 JavaScript 反应性时才可用。例如,带有图像的静态文本。没有 JS 动画,没有 React Context,没有 React hooks,没有 DOM 操作,服务器上没有窗口使用!
  9. 您仍然可以从服务器获取任何服务器端组件(例如Suspense文档dynamic中的示例)。但不要欺骗自己。这更像是具有“组件”响应类型的 get-request。该组件将被插入到您的客户端树中。这接近于“获取一些原始 html,然后使用 with 将其插入到组件树中dangerouslySetInnerHTML”的情况。当然,在这种情况下,您的服务器数据首先在服务器上呈现,因此可以使用数据库查询。它可以使用一些其他服务器组件,但同样,服务器上没有客户端功能。

我有很多项目,但没有一个项目可以做到至少 50% 仅服务器化。我个人并不希望“仅服务器”组件能够神奇地解决任何优化问题或显着加速任何普通网站(如 Medium.com 或 Reddit)。

我希望我的深入解释和各种例子能够对某人有所帮助。


Sac*_*Som 8

根据我的理解,只有当包含任何仅服务器代码(例如,数据库调用或使用组件级 async/await )时,我们才能导入Server Components内部。Client ComponentsServer Components

在您的情况下,InitView没有任何特定于服务器的代码,因此将其导入客户端组件内,可能会将其推断为Client Component.

您可以在 Next.js 的测试版文档中找到它 -链接