Closed9

Next conf 2022 個人まとめ

kobokobo

Keynote

Next.js v13 - Dynamic without limits

Next.js v13

  • Compiler Infrastructure
  • Renderling Infrastructure
  • Component toolkit

[α] Turbopack - Introducing the successor to Webpack, written in Rust

[β] New Next.js Router

appディレクトリに配置することで適応可能

  • layout.js: サブディレクトリを含むページを跨いでの状態を共有することが可能
    • rerenderもされない
  • page.js: ページを作成する
  • loading.js: データフェッチ中などローディング状態を表す時に用いられる
  • error.js: エラーが発生した際に用いられる

[Stable] New Next/Image

  • Native lazy-loading
  • Better performance
  • Improved accessibility
  • Reduced size

[β] New Next/Font

kobokobo

やってみながら確認する

Turbopack

TurborepoのサイトにTurbopackが既にある

環境構築

既にnode.jsは入ってる前提で

npx create-next-app --example with-turbopack
npm run dev

とすると、next dev --turboで立ち上げるようになってる

めちゃくちゃ速い、、(多分、体感

本当はコンポーネントを増やしてみて、Webpackと比較したりすると良いんだろうけど、この時点で体感でわかるぐらい速い気がする

kobokobo

New Next.js Router

appディレクトリに置くことで適応されるらしいので、作ってみる

ともったけど、サンプルアプリにめちゃくちゃ入っていたので中身を確認していく

kobokobo

layout.tsx

配下のディレクトリで共有されるレイアウト
ヘッダー、フッター、サイドバーなどのコンポーネントを呼び出すのが良さそう

ページ遷移の際、再レンダリングがなく、つまり状態が保持される

という感じになるっぽい

コード確認

サンプルプロジェクト直下にあるlayout.tsxでは

export default function RootLayout({ children }: { children: any }) {
 ...
          <div className="col-start-2">
            <GlobalNav />
          </div>

          <div className="col-start-3 space-y-6">
            <AddressBar />

            <div className="rounded-xl border border-zinc-800 bg-black p-8">
              {children}
            </div>
          </div>
 ...

みたいな感じ

childrenにページやローディング、エラーが入るんだと思う

childrenの型がany...

まだ型の整備が終わってないのかな

kobokobo

template.tsx

layoutとすごくよく似ていて、共通のレイアウトを組むためのもの
ただ、layoutと違い、再レンダリングを行う。つまり、ステートを共有しないレイアウト

というイメージ

特に理由がない限りはlayout.tsxの方を使ってね

と注意書きがある

使い所

  • CSSのアニメーションを仕込みたい(マウント、アンマウントさせたい)
  • useState、useEffectを使いたい共通レイアウト
    • useEffectによるログ発火とか
  • フレームワークデフォルトの挙動を変更したい時
    • よくわかっていない(Suspenseの挙動変更について書かれていたので調べる

コード確認

バウンダリーのアニメーションをつけるためのコードで使われてた

app/layout/template.tsx
import { Boundary } from '@/ui/Boundary';
import React from 'react';

export default function Template({ children }: { children: React.ReactNode }) {
  return <Boundary>{children}</Boundary>;
}
ui/Boundary
export const Boundary = ({
  children,
  labels = ['children'],
  size = 'default',
  color = 'default',
  animateRerendering = true,
}: {
  children: React.ReactNode;
  labels?: string[];
  size?: 'small' | 'default';
  color?: 'default' | 'pink' | 'blue' | 'violet' | 'cyan' | 'orange';
  animateRerendering?: boolean;
}) => {
  return (
 ...
  );
};

kobokobo

error.tsx

errorがthrowされた時にレンダリングされる

コード例

use clientというのはよくわからないけど、第一引数にerrorオブジェクト、第二引数にreset関数(元のページを再度読み込む関数)が与えられるっぽい

'use client';

import { Boundary } from '@/ui/Boundary';
import Button from '@/ui/Button';
import React from 'react';

export default function Error({ error, reset }: any) {
  React.useEffect(() => {
    console.log('logging error:', error);
  }, [error]);

  return (
    <Boundary labels={['Home page Error UI']} color="pink">
      <div className="space-y-4">
        <div className="text-sm text-vercel-pink">
          <strong className="font-bold">Error:</strong> {error?.message}
        </div>
        <div>
          <Button onClick={() => reset()}>Try Again</Button>
        </div>
      </div>
    </Boundary>
  );
}

サンプルでは、ページの方で、

export default function BuggyButton() {
  const [clicked, setClicked] = React.useState(false);

  if (clicked) {
    throw new Error('Oh no! Something went wrong.');
  }

  return (
    <Button
      kind="error"
      onClick={() => {
        setClicked(true);
      }}
    >
      Trigger Error
    </Button>
  );
}

こういうコンポーネントを用意して、クリックしたらerrorがthrowされるようになっている

kobokobo

page.tsx

これは今までのページコンポーネントと同じような役割

大きく違うのは、下に書くloadingの制御部分あたりだろうか

kobokobo

loading.tsx

データがまだ整っていない状態にレンダリングされるもの

コード確認

通常であれば、こんな感じでSuspenseのfallbackとしてローディングコンポーネントを流し、

<>
  <Suspense fallback={<Loading />}>{children}</Suspense>
</>

children側でpromiseをthrowさせることでローディングコンポーネントを表示することになりそうだが、これをラップしてくれているみたい

なので、使用者はSuspenseを書くことなくloading.tsxを用意しておくだけで良いという感じになりそう。

ページ側では以下のような感じになっていた

// @ts-ignore
import { use } from 'react';
import {
  fetchCategoryBySlug,
  PageProps,
  type Category,
} from '@/lib/getCategories';
import { SkeletonCard } from '@/ui/SkeletonCard';

const fetchCategory = async (
  categorySlug: string | undefined,
): Promise<Category | undefined> => {
  // artificial delay
  await new Promise((resolve) => setTimeout(resolve, 3000));

  if (!categorySlug) return;

  return await fetchCategoryBySlug(categorySlug);
};

export default function Page({ params }: PageProps) {
  const category = use(fetchCategory(params.categorySlug));
  if (!category) return null;

  return (
    <div className="space-y-4">
      <div className="text-xl font-medium text-zinc-500">{category.name}</div>

      <div className="grid grid-cols-3 gap-6">
        {Array.from({ length: category.count }).map((_, i) => (
          <SkeletonCard key={i} />
        ))}
      </div>
    </div>
  );
}

useがpromiseを返す関数を引数に受け取ることで、データフェッチがまだ終わっていない時は、useがpromiseをthrowしてくれる

これにより、suspenseにloading.tsxが挿入される

kobokobo

next/image

next/imageが強化された

変わったこと

  • クライアントサイドに必要なJavaScript量が減った
  • スタイルと設定が簡単になった
  • altが必須になったことでアクセシビリティが上がった
  • Webプラットフォームとの整合性が取れるようになった
  • ネイティブレイジーロードによりハイドレーションが不要となり高速化した

@next/font

フォントのサポートがされた

  • カスタムフォントも含め、フォントの自動最適化ができる
  • 外部へのネットワークリクエストをなくし、パフォーマンスとプライバシーを向上させた
  • あらゆるフォントファイルの自動セルフホスティング機能
  • CSSのsize-adjustプロパティを使い、自動的にレイアウトのずれをゼロにするように

next/linkが改善された

next/linkの中にaタグが統合されたことで、

<Link href="/about">
  <a>About</a>
</Link>

みたいなコードを書かなくてよくなった

このスクラップは2022/10/27にクローズされました