Кратък преглед на бета функциите на Next.js 13

Next.js 13 се появи по малко объркващ начин. Добавени са много забележителни неща; обаче, голяма част все още е бета. Въпреки това бета функциите ни дават важни сигнали за това как ще бъде оформено бъдещето на Next.js, така че има основателни причини да ги следите отблизо, дори ако ще чакате да ги приемете.

Тази статия е част от поредица от изживявания относно бета функциите. Нека днес играем със сървърни компоненти.

Превръщането на сървърните компоненти в опция по подразбиране е може би най-смелата промяна, направена в Next.js 13. Целта на сървърния компонент е да намали размера на JS, изпращан на клиента, като запази кода на компонента само от страната на сървъра. Т.е. изобразяването се случва и се случва само от страната на сървъра, дори ако зареждането на компонента се задейства от страната на клиента (чрез маршрутизиране от страна на клиента). Това е доста голяма промяна на парадигмата.

За първи път се запознах с React Server Components преди повече от година от това видео (гледайте по-късно, доста е дълго):

Дотогава се чувства доста „проучвателно“, така че бях шокиран, когато видях, че Next.js вече залага бъдещето си на него сега. Времето лети и фантастичните инженери от React трябва да са свършили наистина страхотна работа, така че създадох лъскав нов проект Next.js 13, за да си поиграя с него.

npx create-next-app@latest --experimental-app --ts --eslint next13-server-components

Нека се забавляваме, като си играем с проекта. Можете да намерите пълния код на проекта тук.

Сървърен компонент

Първата забелязана разлика е, че нова папка app сега се намира заедно с нашия стар приятел page. Ще запазя промените в маршрутизирането в друга статия, но това, което си струва да се спомене засега, е, че всеки компонент под папката app по подразбиране е сървърен компонент, което означава, че се изобразява от страната на сървъра и кодът му остава на сървъра страна.

Нека сега създадем нашия първи сървърен компонент:

// app/server/page.tsx

export default function Server() {
    console.log('Server page rendering: this should only be printed on the server');
    return (
        <div>
            <h1>Server Page</h1>
            <p>My secret key: {process.env.MY_SECRET_ENV}</p>
        </div>
    );
}

Ако осъществите достъп до /server маршрута, независимо дали чрез ново зареждане на браузъра или чрез маршрутизиране от страна на клиента, ще видите само реда на журнала, отпечатан в конзолата на вашия сървър, но никога в конзолата на браузъра. Стойността на променливата на средата се извлича и от страната на сървъра.

Разглеждайки мрежовия трафик в браузъра, ще видите, че съдържанието на сървърния компонент се зарежда чрез отдалечено повикване, което връща октетен поток от JSON данни на резултата от рендиране:

{
    ...
    "childProp": {
        "current": [
            [
                "$",
                "div",
                null,
                {
                    "children": [
                        ["$", "h1", null, { "children": "Server Page" }],
                        [
                            "$",
                            "p",
                            null,
                            {
                                "children": ["My secret key: ", "abc123"]
                            }
                        ]
                    ]
                }
            ]
        ]
    }
}

Изобразяването на сървърен компонент е буквално извикване на API, за да получите сериализиран виртуален DOM и след това да го материализирате в браузъра.

Най-важното нещо, което трябва да запомните е, че сървърните компоненти са за изобразяване на неинтерактивно съдържание, така че няма манипулатори на събития, няма кукички на React и няма API само за браузър.

Най-значимото предимство е, че имате свободен достъп до всеки backend ресурс и тайни в сървърните компоненти. По-безопасно е (данните не изтичат) и по-бързо (кодът не изтича).

Клиентски компонент

За да направите клиентски компонент, ще трябва да го маркирате изрично с use client:

// app/client/page.tsx

'use client';

import { useEffect } from 'react';

export default function Client() {
    console.log(
        'Client page rendering: this should only be printed on the server during ssr, and client when routing'
    );

    useEffect(() => {
        console.log('Client component rendered');
    });

    return (
        <div>
            <h1>Client Page</h1>
            {/* Uncommenting this will result in an error complaining about inconsistent
            rendering between client and server, which is very true */}
            {/* <p>My secret env: {process.env.MY_SECRET_ENV}</p> */}
        </div>
    );
}

Както може би вече очаквате, това ви дава поведение, подобно на предишните версии на Next.js.

Когато страницата се зареди за първи път, тя се визуализира от SSR, така че трябва да видите първия журнал в конзолата на сървъра; по време на маршрутизиране от страна на клиента, и двете регистрационни съобщения ще се появят в конзолата на браузъра.

Смесете и комбинирайте

Една от най-големите разлики между сървърния компонент и SSR е, че SSR е на ниво страница, докато сървърният компонент, както казва името му, е на ниво компонент. Това означава, че можете да смесвате и съчетавате сървърни и клиентски компоненти в дърво за изобразяване, както желаете.

// A server page containing client component and nested server component

// app/mixmatch/page.tsx
import Client from './client';
import NestedServer from './nested-server';

export default function MixMatchPage() {
    console.log('MixMatchPage rendering');
    return (
        <div>
            <h1>Server Page</h1>
            <div className="box">
                <Client message="A message from server">
                    <NestedServer />
                </Client>
            </div>
        </div>
    );
}
// app/mixmatch/client.tsx
'use client';

import { useEffect } from 'react';

export default function Client({
    message,
    children,
}: {
    message: string;
    children: React.ReactNode;
}) {
    console.log('Client component rendering');

    return (
        <div>
            <h2>Client Child</h2>
            <p>Message from parent: {message}</p>
            <div className="box-red">{children}</div>
        </div>
    );
}
// app/mixmatch/nested-server.tsx

export default function NestedServer() {
    console.log('Nested server component rendering');
    return (
        <div>
            <h3>Nested Server</h3>
            <p>Nested server content</p>
        </div>
    );
}

В смесен сценарий като този сървърните и клиентските компоненти се изобразяват независимо и резултатите се събират от React runtime. Реквизитите, предавани от сървърните компоненти към клиентските, се сериализират в мрежата (и трябва да могат да се сериализират).

Компонентите на сървъра могат да се дегенерират

Едно внимание, което трябва да вземете, е, че ако сървърен компонент се импортира директно в клиентски, той тихо се дегенерира в клиентски компонент.

Нека прегледаме леко предишния пример, за да го наблюдаваме:

// app/degenerate/page.tsx

import Client from './client';

export default function DegeneratePage() {
    console.log('Degenerated page rendering');
    return (
        <div>
            <h1>Degenerated Page</h1>
            <div className="box-blue">
                <Client message="A message from server" />
            </div>
        </div>
    );
}
// app/degenerate/client.tsx

'use client';

import NestedServer from './nested-server';

export default function Client({ message }: { message: string }) {
    console.log('Client component rendering');

    return (
        <div>
            <h2>Client Child</h2>
            <p>Message from parent: {message}</p>
            <div className="box-blue">
                <NestedServer />
            </div>
        </div>
    );
}
// app/degenerated/nested-server.tsx

export default function NestedServer() {
    console.log('Nested server component rendering');
    return (
        <div>
            <h3>Degenerated Server</h3>
            <p>Degenerated server content</p>
        </div>
    );
}

Ако проверите дневника, ще видите, че NestedServer е „дегенерирал“ и сега се изобразява от браузъра.

По-добро бъдеще ли е?

Next.js прави всичко възможно, за да премести нещата към сървърната страна, точно както хората правеха уеб разработката преди две десетилетия. Така че сега завършваме пълен кръг, но със значително подобрен опит за разработка и опит за крайни потребители.

За крайните потребители това е ясна победа, тъй като изчисленията от страна на сървъра са по-бързи и по-надеждни. Резултатът ще бъде много по-бързо боядисване на първо съдържание.

За разработчиците промяната на парадигмата ще бъде умствено предизвикателна, примесена с объркване, грешки и анти-модели. Ще бъде адско пътуване.

Благодаря за четенето.

Want to Connect?

I'm the creator of ZenStack, a toolkit that supercharges
Prisma ORM with a powerful access control layer and 
unleashes its full potential for full-stack development.
Our goal is to let you save time writing boilerplate code 
and focus on building what matters - the user experience.