Next conf 2022 個人まとめ
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
[Stable] Improve Next/Link
やってみながら確認する
Turbopack
TurborepoのサイトにTurbopackが既にある
環境構築
既にnode.jsは入ってる前提で
npx create-next-app --example with-turbopack
npm run dev
とすると、next dev --turbo
で立ち上げるようになってる
めちゃくちゃ速い、、(多分、体感
本当はコンポーネントを増やしてみて、Webpackと比較したりすると良いんだろうけど、この時点で体感でわかるぐらい速い気がする
New Next.js Router
app
ディレクトリに置くことで適応されるらしいので、作ってみる
ともったけど、サンプルアプリにめちゃくちゃ入っていたので中身を確認していく
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...
まだ型の整備が終わってないのかな
template.tsx
layoutとすごくよく似ていて、共通のレイアウトを組むためのもの
ただ、layoutと違い、再レンダリングを行う。つまり、ステートを共有しないレイアウト
というイメージ
特に理由がない限りはlayout.tsx
の方を使ってね
と注意書きがある
使い所
- CSSのアニメーションを仕込みたい(マウント、アンマウントさせたい)
- useState、useEffectを使いたい共通レイアウト
- useEffectによるログ発火とか
- フレームワークデフォルトの挙動を変更したい時
- よくわかっていない(Suspenseの挙動変更について書かれていたので調べる
コード確認
バウンダリーのアニメーションをつけるためのコードで使われてた
import { Boundary } from '@/ui/Boundary';
import React from 'react';
export default function Template({ children }: { children: React.ReactNode }) {
return <Boundary>{children}</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 (
...
);
};
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されるようになっている
page.tsx
これは今までのページコンポーネントと同じような役割
大きく違うのは、下に書くloadingの制御部分あたりだろうか
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が挿入される
next/image
next/imageが強化された
変わったこと
- クライアントサイドに必要なJavaScript量が減った
- スタイルと設定が簡単になった
- altが必須になったことでアクセシビリティが上がった
- Webプラットフォームとの整合性が取れるようになった
- ネイティブレイジーロードによりハイドレーションが不要となり高速化した
@next/font
フォントのサポートがされた
- カスタムフォントも含め、フォントの自動最適化ができる
- 外部へのネットワークリクエストをなくし、パフォーマンスとプライバシーを向上させた
- あらゆるフォントファイルの自動セルフホスティング機能
- CSSのsize-adjustプロパティを使い、自動的にレイアウトのずれをゼロにするように
next/link
next/linkが改善された
next/linkの中にaタグが統合されたことで、
<Link href="/about">
<a>About</a>
</Link>
みたいなコードを書かなくてよくなった