TypescriptをGoに変換して覚えてみる
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
Golangに興味があるので、普段使っているTypescriptで行う処理をどのように描くことができるのか、覚えるために記録してみます。
Golangとは
Golang、またはGoはGoogleが開発した静的型付けされたコンパイル言語です。Goはシンプルさと効率性を重視して設計されており、ソフトウェアの開発を迅速かつ効率的に行うことができる。
また、Goは並行処理をサポートしており、複数のタスクを同時に実行することが可能である。
早速はじめてみる
TypescriptのTypeの定義を変換:
Typescript
type TypeName = { name: string; age: number; is_member: boolean; };
type TypeName struct { Name string Age int IsMember bool }
- オブジェクトの場合、structをつける
- :(カンマ)と;(セミコロン)が不要
- stringは同じ、number -> int, boolean -> boolになる(まだまだありそうですが)
map関数の代用:
Typescript
let numbers = [1, 2, 3, 4, 5]; let squares = numbers.map(num => num * num);
numbers := []int{1, 2, 3, 4, 5} squares := make([]int, len(numbers)) for i, num := range numbers { squares[i] = num * num }
- Golangでは、
map
関数の代わりにfor
ループを使用する。 - 配列の定義方法がint(Typeです!){1,2,3,4,5}(実際の中身です!)
なんだか見慣れないmakeだのrangeだの出てきてるので、友達に聞いてみたので以下にまとめます。
numbers := []int{1, 2, 3, 4, 5}
: これはGoで整数のスライスを作成しています。:=は変数宣言と同時に初期化を行うためのショートハンドです。
squares := make([]int, len(numbers))
: make関数は組み込み関数で、スライスやマップ、チャネルなどの動的サイズの型を初期化します。ここでは、numbersと同じ長さの整数型のスライスを作成しています。
for i, num := range numbers
: rangeキーワードはGoのforループで使用され、スライスやマップを反復処理します。rangeはインデックスと値の両方を返します。ここでは、iはインデックス(0から始まる)で、numはそのインデックスに対応する値です。
squares[i] = num * num
: これは各要素を二乗して、結果をsquaresスライスの対応する位置に格納しています。
ExportとImportについて:
Export
Typescript
export const exportFunc = () => {};
Golangでは、大文字で始まる名前(関数、型、変数など)はエクスポートされ、パッケージ外からアクセス可能である。
小文字で始まる名前はエクスポートされず、同じパッケージ内からのみアクセス可能である。
Golang
package main // Exported function func SayHello() { fmt.Println("Hello, world!") } // Unexported function func sayGoodbye() { fmt.Println("Goodbye, world!") }
上記の例では、SayHello
関数は大文字で始まるためエクスポートされ、パッケージ外からアクセス可能ですが、sayGoodbye
関数は小文字で始まるためエクスポートされず、同じパッケージ内からのみアクセス可能となる。
Import:
Typescript
import importFunc from '../import'
Golangでは、import
ステートメントを使用して行います。例えば、fmt
パッケージをインポートするには次のようにする
package main import "fmt" func main() { fmt.Println("Hello, world!") }
上記の例では、fmt
パッケージがインポートされており、その中のPrintln
関数が使用できる。
AppRouterとSuspenseについて
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
AppRouterとSuspenseについて
AppRouterのとSuspenseの組み合わせを使ったことなかったので、試しに実装してみます。
出オチすぎますが、以下記事が個人的にめちゃくちゃわかりやすかったです。
AppRouter
AppRouterについては以前の記事でつらつら書いているので、そちらを参考にしてもらえたら幸いです。
Suspense
SuspenseとはReact18から追加された機能で、子コンポーネントの読み込みを完了するまでフォールバックを表示できます。
<Suspense fallback={<Loading />}> <SomeComponent /> </Suspense>
簡単に表現すると、子のコンポーネントがサスペンド(一時停止)した際に、フォールバックするので、データの取得を担当するコンポーネントがデータ取得までの間、Loading画面を出せるというものです。
trycatch分を書くのと似ています。
import { Suspense } from 'react'; import Albums from './Albums.js'; import Biography from './Biography.js'; import Panel from './Panel.js'; export default function ArtistPage({ artist }) { return ( <> <h1>{artist.name}</h1> <Suspense fallback={<Loading />}> <Biography artistId={artist.id} /> <Panel> <Albums artistId={artist.id} /> </Panel> </Suspense> </> ); } function Loading() { return <h2>🌀 Loading...</h2>; }
これは公式のSuspenseの使用例ですが、子コンポーネントは複数可能で、どれか一つでもサスペンドした場合はフォールバックとなります。
AppRouterとの組み合わせ
とは言っても、使い方は上記で説明した通りです。
import { Suspense } from "react"; import { ChildrenServerComponent } from "@/components/ChildrenServerComponent"; import { Squares } from "@/components/Squares"; import Link from "next/link"; import { Loading } from "@/components/Loading"; export default async function Home() { return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <Suspense fallback={<Loading />}> <ChildrenServerComponent /> </Suspense> <Squares /> </div> ); }
ChildrenServerComponentがサスペンドしている時はフォールバックされるので、Loadingが出ます。
デザイン
紹介になりますが、どの記事も淡白UIの紹介が多かったので、以下記事は確かに!と参考になりました。
終わりに
実際に色々と使ってみて、慣れないとなという感じです。
ここまでみていただきありがとうございました!
Nextjs AppRouter個人的気になる点をまとめる
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
今回の内容
AppRouterの気になった機能をつらつら書いていきます。
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(色々試す2) - zare926のブログ
NextjsのAppRouterのRoute Handlers(API)ついて書きました。
SEOタグの設定
import type { Metadata } from 'next' export const metadata: Metadata = { title: 'タイトルです!', description: '説明です!', }
PageRouterの時は以下の方法でLighthouseが100点を出すそうです。
これはAppRouterいい仕事してますね。
何回も同じFetchすな!(Request Memoization)
簡単にいうと親子で同じFetchをしていたら1個にまとめてくれます!
1回しかFetchせず、データを使いまわしてくれます。
これはちょっと前にcacheを調べた時に起きてましたね。
確か、fetchの設定は親が優先されたはずです。
とりあえず優しい
Server Actions
まず、stableな機能ではないので注意です。
import db from './db'; import { redirect } from 'next/navigation'; async function create(formData: FormData) { 'use server'; const post = await db.post.insert({ title: formData.get('title'), content: formData.get('content'), }); redirect(`/blog/${post.slug}`); } export default function Page() { return ( <form action={create}> <input type="text" name="title" /> <textarea name="content" /> <button type="submit">Submit</button> </form> ); } //https://nextjs.org/blog/next-13-4#server-actions-alpha
簡潔にまとめると、APIで実装したいCreate処理などを直接書き込めちゃいます。
'use server';
こいつが肝です
関数が複数ある場合は、ファイルの一番上に定義することも可能です。
てことはクライアントサイドには、見えないコードができるはずなので、見せたくないものを見せないようになどの使い方もできそうです。
fetch関数も使わなくてすみます。
一応設定が必要
next.config.js
module.exports = { experimental: { serverActions: true, }, }
サーバーコンポーネントでは、UIコンポーネントが使えない!!
MUIやChakra UIが使えません!
サーバーでは動かせないemotionを使っているからだそうです。
クライアントコンポーネントで使用することはできます。
Chakra UIでこうしろと書いてありました。
最後に
調べて気になったのはこの辺りでしたね。
統括してまとめてるサイトは結構あるんですが、メモして自分も記憶出るようにと書いてみました。
ここまで読んでいただいてありがとうございました!!
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(色々試す2)
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
お題と経緯
NextjsのPageRouterからAppRouterに乗り換えてみてパフォーマンスを測ってみたいし、使ってもみたいなってことで、
今回はFirebaseのhostingでAppRouterがどこまで機能するかを、パフォーマンス向上の一環として試そうと思います!
まずはAppRouterでサンプルのAppを作成しつつ、備忘録で説明を書いていこうと思います。
今回の内容
AppRouterのRoute Handlers(API)について調べていきたいと思います!
Routing: Route Handlers | Next.js
使用した技術
Category | Technology Stack |
---|---|
Frontend | Nextjs,React,Typescript |
Backend | Typescript,Node,CloudFunction |
Database | Firestore |
Design | XD |
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(色々試す) - zare926のブログ
NextjsのAppRouterでサーバーコンポーネントやクライアントコンポーネントについて書きました。
Route Handlersとは
Nextjsの13.4からstableとなったAppRouterで実装できるAPIですね。
PagesRouterの時はAPI Routesでしたね。
APIRouteとRoute Handlersの違い
一つ目はファイルパスですね。
API Routes
pages/api/sample-api.ts
pages/api/[slug].ts
export default function handler(req, res) { res.status(200).json({ name: 'Michelle' }); }
Route Handlers
app/sample-api/route.ts
app/sample-api/[slug]/route.ts
import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ name: 'Michelle' }); }
ざっくりとこの様な変更があるようですね。
実際にはAPI Routesではreq.query
などURLのパラメーターを展開できたりしましたが、どの様に変わるのかを調べたいと思います。
実際に作成してみる
app/[slug]/route.ts
import { NextResponse, NextRequest } from "next/server"; export async function GET( request: NextRequest, { params }: { params: { slug: string } } ) { console.log(params); console.log(request); return NextResponse.json({ data: new Date().toISOString() }); }
こんな感じでGETはrequestとparamsを受け取れる様ですので、中身を見てみます。
[slug]の部分は以下にします。
await fetch("/slug-url");
わかりづらいですが、{ slug: 'slug-url' }
がconsole.log(params);
で、それ以下がconsole.log(request);
ですね。
pathはこの方法で取れますね。
URLのクエリパラメーターの取り方も変わってます。
const params = { hoge: "1234567", fuga: String(76454321), }; const parameter = new URLSearchParams(params); await fetch(`/slug-url?${parameter}`);
import { NextRequest, NextResponse } from "next/server"; export async function GET( request: NextRequest, { params }: { params: { slug: string } } ) { console.log(params); const searchParams = request.nextUrl.searchParams; const hoge = searchParams.get("hoge"); console.log(hoge); // 1234567とログに出る。 const fuga = searchParams.get("fuga"); console.log(fuga); // 76454321とログに出る。 return NextResponse.json({ data: new Date().toISOString() }); }
もちろんrequest.body
もあります。
クライアントサイドから動的な値でAPIの実行内容を変えたい場合も、PagesRouter同様にできますね。
最後に
変更点は特に違和感なく使えたかなと思います。
paramsの取得はPagesRouterの方が楽でしたね。
AppRouterの場合、searchParams.get()
後の型推論はstring | null
だったので、req.query
から抜き取るよりも型がキッチリしてますね。
ファイル名もpages.ts同様、決まりがあるので頭を少しでも使わ内容にできる点は満足です!
ここまで読んでいただきありがとうございました!
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(色々試す)
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
お題と経緯
NextjsのPageRouterからAppRouterに乗り換えてみてパフォーマンスを測ってみたいし、使ってもみたいなってことで、
今回はFirebaseのhostingでAppRouterがどこまで機能するかを、パフォーマンス向上の一環として試そうと思います!
まずはAppRouterでサンプルのAppを作成しつつ、備忘録で説明を書いていこうと思います。
今回の内容
画像の圧縮方法を記録していきたいと思います!
使用した技術
Category | Technology Stack |
---|---|
Frontend | Nextjs,React,Typescript |
Backend | Typescript,Node,CloudFunction |
Database | Firestore |
Design | XD |
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(fetch) - zare926のブログ
NextjsのAppRouterでSGやSSR、ISRのfetchについてやってみました。
色々試す!
今回は色々試す回にします。
まずはクライアントコンポーネントから動的なfetchをしてみます。
FetchComponentとButtonのコンポーネントを作成
FetchComponent.tsx
"use client"; import React, { useState } from "react"; import { Button } from "./Button"; const FetchComponent = () => { const [user, setUser] = useState<{ id: number; name: string }>(); const testFunc = async () => { const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1; const response = await fetch( `https://jsonplaceholder.typicode.com/users/${number}` ); const user: { id: number; name: string } = await response.json(); return user; }; const fetchUser = async () => { const response = await testFunc(); setUser(response); }; return ( <div> <Button text="fetch button" onClick={fetchUser} /> <p> クライアントコンポーネントからfetchした値 : {user && <span>{user.name}</span>} </p> </div> ); }; export default FetchComponent;
Button.tsx
"use client"; import React from "react"; type Props = { text: string; onClick?: () => void; }; export const Button = (props: Props) => { const { text, onClick } = props; return ( <button className="bg-gray-200 hover:bg-gray-300 active:bg-gray-400 border border-gray-400 p-2 transition-all duration-300 ease-in-out" onClick={() => { onClick && onClick(); }} > {text} </button> ); };
import { FetchComponent } from "@/components/FetchComponent"; import { Squares } from "@/components/Squares"; import Link from "next/link"; export default async function Home() { const response = await fetch(`http://localhost:3000/sample-date-get`, { next: { revalidate: 5 }, }); const data = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.data}</p> <FetchComponent /> <Squares /> </div> ); }
json placeholderでuserの名前を引っ張ってきてますが、うまく動いていますね。
ちなみにクライアントサイドでのfetch関数にISRの設定をしてもcacheはされなかったです。
次にサーバーコンポーネントからfetchをしてcacheの設定をしてみます。
ChildrenServerComponent.tsx
import React from "react"; export const ChildrenServerComponent = async () => { const response = await fetch(`http://localhost:3000/sample-date-get`, { next: { revalidate: 20 }, }); const data = await response.json(); return ( <div> <p> サーバーサイドコンポーネントからfetchした値 :<span>{data.data}</span> </p> </div> ); };
page.tsx
import { ChildrenServerComponent } from "@/components/ChildrenServerComponent"; // import { FetchComponent } from "@/components/FetchComponent"; import { Squares } from "@/components/Squares"; import Link from "next/link"; export default async function Home() { const response = await fetch(`http://localhost:3000/sample-date-get`, { next: { revalidate: 5 }, }); const data = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.data}</p> <ChildrenServerComponent /> <Squares /> </div> ); }
しっかりとサーバーでfetchされ表示されました。
親のサーバーコンポーネントではrevalidateを5秒に設定し、子のサーバーコンポーネントでは20秒にしましたが、同じAPIだったからなのか、親の5秒に引っ張られました。
逆に親の5秒をコメントアウトしたところ、親を含めて20秒の設定になりました。
APIを分けた場合、それぞれの設定が生かされました。
てとこですかね。
最後にクライアントコンポーネントの子にサーバーコンポーネントを設置してみます。
FetchComponent.tsx
"use client"; import React, { useState } from "react"; import { Button } from "./Button"; import { ChildrenServerComponent } from "./ChildrenServerComponent"; export const FetchComponent = () => { const [user, setUser] = useState<{ id: number; name: string }>(); const testFunc = async () => { const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1; const response = await fetch( `https://jsonplaceholder.typicode.com/users/${number}` ); const user: { id: number; name: string } = await response.json(); return user; }; const fetchUser = async () => { const response = await testFunc(); setUser(response); }; return ( <div> <Button text="fetch button" onClick={fetchUser} /> <p> クライアントコンポーネントからfetchした値 : {user && <span>{user.name}</span>} </p> <ChildrenServerComponent /> </div> ); };
page.tsx
import { FetchComponent } from "@/components/FetchComponent"; import { Squares } from "@/components/Squares"; import Link from "next/link"; export default async function Home() { const response = await fetch(`http://localhost:3000/sample-date-get`, { next: { revalidate: 5 }, }); const data = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.data}</p> <FetchComponent /> <Squares /> </div> ); }
SC<CS<SC(async/await有)という設定ですが、結論これはダメでした。
クライアントコンポーネントは、コンポーネント単位でasync/awaitを使えないので、子のコンポーネントがサーバーサイドでasync/awaitを使用しているとエラーが出ます。
サーバーコンポーネントがasync/awaitを使っていなければ、今回の様な親子関係も可能です。
ちなみにSC<SC<CSは問題なく動きます。
この辺りは慣れが必要そうですね!
最後に
色々と把握できた回で満足でした!
ちなみに今回出たエラーを翻訳すると
エラー:async/awaitはクライアント・コンポーネントではまだサポートされていません。このエラーは、本来サーバ用に書かれたモジュールに誤って `'use client'` を追加してしまった場合によく起こります。
なんですが、将来的に対応する様になるんですかね。
そうするとかなり自由度があるようなないような。。。
動的なアプリを作る場合は結構考えものです。
ここまでみていただいてありがとうございました!!!
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(fetch)
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
お題と経緯
NextjsのPageRouterからAppRouterに乗り換えてみてパフォーマンスを測ってみたいし、使ってもみたいなってことで、
今回はFirebaseのhostingでAppRouterがどこまで機能するかを、パフォーマンス向上の一環として試そうと思います!
まずはAppRouterでサンプルのAppを作成しつつ、備忘録で説明を書いていこうと思います。
今回の内容
AppRouterで色々なfetchしていきたいと思います!
使用した技術
Category | Technology Stack |
---|---|
Frontend | Nextjs,React,Typescript |
Backend | Typescript,Node,CloudFunction |
Database | Firestore |
Design | XD |
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter - zare926のブログ
NextjsのAppRouterで静的コンテンツを表示するところまでやってみました。
AppRouterでfetchする
SGやだとgetStaticProps、SSRだとgetServerSidePropsを定義し、propsでJSXに値を渡していましたが、AppRouterのfetchはJSX内に直接書くことができます。
import { Squares } from "@/components/Squares"; import Link from "next/link"; export default async function Home() { const response = await fetch("https://jsonplaceholder.typicode.com/users/1"); // ここ const data: { name: string } = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.name}</p> // 定義したデータをそのまま使える <Squares /> </div> ); }
バケツリレーがなくなりますね。
ちなみにこの実装だとSGになり、ビルド時に1回だけfetchをしてくれます。
サーバーコンポーネントでSG,SSR,ISRの書き方
SSRの場合は以下の様に書きます。
import { Squares } from "@/components/Squares"; import Link from "next/link"; export const fetchCache = "default-no-store"; export default async function Home() { const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1; const response = await fetch( `https://jsonplaceholder.typicode.com/users/${number}` ); const data: { name: string } = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.name}</p> <Squares /> </div> ); }
build後もサーバーサイドでやり取りしてくれるので、動的な表示が可能になります。
他にも、Dynamic Functionというものがあり、Server Component 内で使える cookies(), headers()のことを指します。
cookies(), headers()を定義するとSSRになります。
import { Squares } from "@/components/Squares"; import Link from "next/link"; import { cookies } from "next/headers"; // export const fetchCache = "default-no-store"; export default async function Home() { const cookieStore = cookies(); const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1; const response = await fetch( `https://jsonplaceholder.typicode.com/users/${number}` ); const data: { name: string } = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.name}</p> <Squares /> </div> ); }
ISRの説明をするためにapiを用意。
src/app/sample-date-get/route.ts
import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ data: new Date().toISOString() }); }
時間でCacheの設定をするのでわかりやすいAPIを準備します。
import { Squares } from "@/components/Squares"; import Link from "next/link"; export const revalidate = 10; // ここ export default async function Home() { const response = await fetch(`http://localhost:3000/sample-date-get`); const data = await response.json(); return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <p>{data.data}</p> <Squares /> </div> ); }
上記の場合はコンポーネント全体で10秒キャッシュとしています。
もしくはfetch関数単位でも設定可能です。
const response = await fetch(`http://localhost:3000/sample-date-get`, { next: { revalidate: 10 }, });
こちらに関してyarn devだとうまくいきますが、yarn buildだとうまくいきません・・・、原因がわかったら記事に追加しておきます。
.next/cache/fetch-cache ここにcacheで下記の様にjsonができているので、機能して欲しいのですが・・・
"status":200,"url":"http://localhost:3000/sample-date-get"},"revalidate":10,"tags":[]}
最後に
少しクセはありますが、cacheが楽に書けるなと思いました。
またページによってSG、ISRなど機能を振り分けしやすいと感じます。
ただISRだけはbuild後、期待通りのcacheではないので、調べないと・・・
果たしてFirebaseでhostingはできるのか・・・?
次回はクライアントコンポーネントから動的な関数を呼ぶ方法を書いてみたいと思ってます。
ここまでみていただきありがとうございました!!!
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
お題と経緯
NextjsのPageRouterからAppRouterに乗り換えてみてパフォーマンスを測ってみたいし、使ってもみたいなってことで、
今回はFirebaseのhostingでAppRouterがどこまで機能するかを、パフォーマンス向上の一環として試そうと思います!
まずはAppRouterでサンプルのAppを作成しつつ、備忘録で説明を書いていこうと思います。
今回の内容
AppRouterについて調べていきたいと思います!
使用した技術
Category | Technology Stack |
---|---|
Frontend | Nextjs,React,Typescript |
Backend | Typescript,Node,CloudFunction |
Database | Firestore |
Design | XD |
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング④ 画像のアップロード圧縮 - zare926のブログ
画像アップロード時の圧縮方法でした!
Nextjs AppRouterとは
簡単にいうとサーバーサイドでコンポーネントのレンダリングをして、HTMLを返してくれる(全部じゃない)ルーティングのベース機能です。
PageRouterからどのように変わったのかは以下記事がわかりやすかったです。
init
$ npx create-next-app@latest Need to install the following packages: create-next-app@13.5.2 Ok to proceed? (y) ✔ What is your project named? … app-router-sample-app ✔ Would you like to use TypeScript? … No / Yes ✔ Would you like to use ESLint? … No / Yes ✔ Would you like to use Tailwind CSS? … No / Yes ✔ Would you like to use `src/` directory? … No / Yes ✔ Would you like to use App Router? (recommended) … No / Yes ✔ Would you like to customize the default import alias? … No / Yes $ cd app-router-sample-app $ code. // 設定していればvscode起動
触っていく!
PageRouterはpageディレクトリの中でルーティングさせてましたが、AppRouterはappディレクトリの中でルーティングをさせます。
またPageRouterからの変更点として以下のようなディレクトリ名かつファイル名である必要があります。
. ├── app │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ └── sample │ └── page.tsx └── components └── Squares.tsx
すでに初期状態からsample/page.tsxとcomponents/Squares.tsxを作成しています。
layout.tsxはPageRouterの_app.tsxの役割って感じですね。
各ディレクトリごと作成できるので、仕様を分けたいページがある場合は便利です。(と解釈している)
各ファイルをみましょう。
page.tsx
import { Squares } from "@/components/Squares"; import Link from "next/link"; export default function Home() { return ( <div className="p-8 relative"> <h1 className="text-gradient-blue-green" data-content="TopPage"> TopPage </h1> <Link href="/sample" className="text-sky-600"> Sampleページへ </Link> <Squares /> </div> ); }
sample/page.tsx
import { Squares } from "@/components/Squares"; import Link from "next/link"; export default function Home() { return ( <div className="p-[100px]"> <h1 className="text-gradient-blue-green" data-content="SamplePage"> SamplePage </h1> <Link href="/" className="text-sky-600"> Topページへ </Link> <Squares /> </div> ); }
components/Squares.tsx
"use client"; import React, { useEffect, useState } from "react"; export const Squares = () => { const [squares, setSquares] = useState< { top: string; left: string; width: string; height: string; transform: string; }[] >([]); useEffect(() => { const generateRandomSquare = () => ({ top: `${Math.random() * 50 + 25}%`, left: `${Math.random() * 100}%`, width: `${Math.random() * 40 + 10}px`, height: `${Math.random() * 40 + 10}px`, transform: `rotate(${Math.random() * 360}deg)`, }); const initialSquares = Array.from({ length: 100 }, generateRandomSquare); setSquares(initialSquares); const interval = setInterval(() => { const newSquares = initialSquares.map(generateRandomSquare); setSquares(newSquares); }, 10000); return () => clearInterval(interval); }, []); return ( <div className="relative h-[1000px]"> {squares.map((square, index) => ( <div key={index} className="border border-black rotate-animation" style={{ position: "absolute", top: square.top, left: square.left, width: square.width, height: square.height, transform: square.transform, transition: "transform 10s, top 10s, left 10s", }} ></div> ))} </div> ); };
実際の動きはこんな感じ
動きましたね!
ということで説明をしていきます。
まず大事なルーティングですが、ディレクトリ名がurlとなり、直下にpage.tsxを作るようになりました。
/sampleでsample/page.tsxが表示されるということですね。
これらはすべてサーバーコンポーネントとなります。
そしてここがよくひっかかる!サーバーコンポーネントはuseStateが使えません。
使いたい場合は、components/Squares.tsxの一番上に定義した"use client";
を設定する必要があります。
これはクライアントコンポーネントですよという宣言になり、このような制約ができているので、最初はひっかかりそう
レンダリングはサーバーからしてしまうので、state更新時にクライアントコンポーネントでないといけないという解釈をしました。
最後に
今回はほぼ準備でしたが、次回以降はサーバーコンポーネント側でfetch関数を扱い、データ取得などを行ってみようと思います。
SGやISRなど切り替わった部分を実装してみて、FirebaseへHostingしてみようと思います。
これがうまくいけば作成しているサイトもAppRouterに切り替え可能なはず!
ちなみにサーバーに負担させるメリット?的ない解説はここに載っていました。
Next.jsの新常識「App Router」を学ぼう! (1/3)|CodeZine(コードジン)
ここまで見ていただいた方ありがとうございました!