Next.js(14.0.4)を触ってみたお話
ここ2日間、Next.jsを触っています。
Vue系統のNuxtは触っているけど、React系の子は触ってないなぁってことで、 Next.jsを触ってみたという次第。
Next全くわからん状態から、プロジェクトを作り、ファイルを配置そのたもろもろを経て、カメラ取得までやってみた。
最後にVercelでデプロイするまで行なってみた。そんな記事。
Nextのバージョンは14.0.4
を使用。(調べているうちに古いバージョンの情報と新しい情報がごっちゃになってややこしかったので書いておきます)
Nextで何をしたのか
今回Nextを使うにあたって挑戦してみたのは以下の5つ。
- Nextでプロジェクトを作成する
- appフォルダ内にファイルを用意してページを作る
- 双方向バインディングをやってみる
- カメラ取得コンポーネントを作って表示してみる
- Vercelでデプロイしてみる
5つもあるので一気に駆け抜けていこう
1. Nextでプロジェクトを作成する
NextはNodeサーバー内での処理、いわゆるSSR(サーバーサイドレンダリング)を用いるため、前提としてNodeが必要となる。
その辺の準備が面倒だったのでGitpodのデフォルト環境を使用しました。
空リポジトリを作って、URLの頭にhttps://gitpod.io#
をつけてアクセスして新規ワークスペースをいい感じに作りました。
さて、できたワークスペースにはNodeがすでに入っているので面倒な環境構築いらずで、コマンド一つでNextプロジェクトを作成します。
npx create-next-app testappnext
コマンドを実行すると、以下のように質問されるのでひとまず全部規定値で作成しました
- nextのプロジェクト作成コマンドを実行してOK?
- TypeScriptを使う?(規定値はyes)
- ESLintを使う?(yes)
- TailWind CSSを使う?(yes)
-
src/
ディレクトリを使う?(no) - App Routerを使う?(yes)
- 規定のインポートエイリアス(@/*)を使う?(no)
こうするとプロジェクトフォルダtestappnext
が出来上がるので、そこに移動してdevサーバーを起動すると、3000番ポートでサーバーが起動する。やったね
cd testappnext
npm run dev
2. appフォルダ内にファイルを用意してページを作る
さて、サーバー起動まで漕ぎ着けたが、Nextではどのようにページを作ろうかという問題がある。
(ちなみに筆者はVue系と同じように適当なファイル名で配置した結果わけがわからないことになってしまった。残念)
ここでNextのバージョンが13以降だと、App Routerが推奨されており、決まった名前のファイルを作る必要があるようだ。
例として、hogehoge.com/camera
というルーティングでサイトのページを作りたいときは、app
フォルダの中にcamera
フォルダを作ることになる。
そしてそのフォルダの中に、page.tsx
という特別な名前のファイルを作成する。
具体的にはこんな感じ
testappnext
├─app
│ └─camera
│ └─page.tsx(`/camera`ページ用)
├─page.tsx(トップページ用)
...
このファイルにページとして出力するコードをかいていくという寸法のようだ。
今回は初めてなので元から用意されているトップページを真似て、以下のように作ってみることにする
export default function Home() {
return(<div><ページとしてレンダリングする内容><div>)
}
ページとしてレンダリングする内容を適当に編集して、3000番ポートの/camera
パスにアクセスすると実際に画面表示がされて、ホットリロードも機能したところまで確認した。
3. 双方向バインディングをやってみる
さて、無事にページを表示することまでは完了した。
Vue系になれている筆者はあの簡単双方向バインディングが大好きなのでNextでも同じことをやっていこうと思う。
Nextではサーバーサイドであらかじめ処理をして軽くしようという思想があるようなので、変数はサーバー側で処理を行っている。
そんなわけで、Nextでの双方向バインディングでページをユーザー側のブラウザで更新したかったら、"このファイルはユーザー側のブラウザで行う処理を書きます"という旨を宣言する必要があるらしい。なるほど。
page.tsx
ファイルの1行目に"use client"
という記述を追加する。
そしてもう一つ、双方向バインドを行いたい場合はuseState
という機能を使うらしい。
まずはインポート。
import {useState} from "react"
双方向バインドするための特殊な変数はこのuseState
を使って生成する。
const [huga, sethuga]=useState<Strin>("<hugaの初期値>")
このコードではhuga
という文字の変数と、sethuga
という名前の関数が作成できる。
このsethuga
関数は、sethuga("<更新するhugaの内容>")
のようにしてレンダリングされる内容を変化させることができる。
変化させる変数huga
は、レンダリングするreturn
中{<変数名>}
の形で記述してあげるといい感じ。
実際に書いたコードはこれ。
"use client"
import {useState, useRef, useCallback, useEffect} from "react"
export default function Home() {
const [huga, sethuga]=useState<String>("")
function change_huga(e: any){
sethuga(e.target.value)
}
return (
<div>
<span>双方向バインドテスト</span><input style={{color: "black"}} onChange={change_huga} type="text"></input>
<div>{huga}</div>
</div>
)
}
HTML中にInputを用意してあげて、それのonChange
イベントで、内容が変化したらInput内容をsethuga
で代入してあげています。
(ネイティヴJSでは、onchange
が全部小文字のところを、Nextで関数を渡したいときはキャメルケースで大文字を混ぜるらしい)
表示されたページのInputに入力するとその内容が反映してくれた。やったね。
専用の関数立てたり、Object
を渡せなかったりするのでちょっと面倒かなと思う反面、双方向するもの/しないものを明確にできる分、効率化やってるって感じがしてかっこいいね(?)
4. カメラ取得コンポーネントを作って表示してみる
さらっと書いているけど、初学者的にはコンポーネント作成
+カメラ取得
の2つの内容が混じっているこのセクション。まずはコンポーネント化からやってみる。
まずはコンポーネントのファイルを作成します。
Nextでは相対パスでimportするのでフォルダ構成はこんな感じ。
testappnext
├─app
│ └─camera
│ ├─page.tsx
│ └─CameraGet.tsx(追加したコンポーネント)
├─page.tsx
...
そしてpage.tsx
でインポート。
import CameraGet from "./CameraGet"
インポートできたらreturn
内でHTMLタグのように使えるのでそのまま記載。なんだか既視感。
return (
<div>
<span>双方向バインドテスト</span><input style={{color: "black"}} onChange={change_huga} type="text"></input>
<div>{huga}</div>
<CameraGet />←追加したコンポーネント
</div>
)
ちなみにここで、コンポーネント名は大文字で始めないと読み込んでくれないらしい。へー
とまぁこれでコンポーネントを表示できるわけなので、コンポーネントの中身を作っていこう。
作成したファイルがこれ。
"use client"
import {useEffect} from 'react'
export default function CameraGet(){
useEffect(()=>{
const elm_video=document.getElementById("camera")! as HTMLVideoElement
navigator.mediaDevices.getUserMedia(
{
audio: false,
video: {
facingMode: {exact: "environment"}
}
})
.then((stream)=>{
elm_video.srcObject=(stream)
console.log(stream.getTracks())
elm_video.play()
}
)
}, [])
return(
<div>
<video id="camera" />
</div>
)
}
カメラの動きとしてはvideo
タグのHTMLエレメントに取得したカメラ画像を貼り付けて表示する形になるいつもの(?)感じなわけだが、getElementById
ってそもそもレンダリング後に動かさなきゃいけないわけです。
そりゃぁ、Elementが描画された後でないと、探そうにも見つかるわけなくエラーになるわけです。
return
で返り値としてhtmlタグを書いているのでこのままでいくと取得できるhtmlElementが無なのでnull
になってしまう。
なので、レンダリング後に動作させる関数として設定しなければなりません。
ここで使うのがuseEffect
。これはレンダリング後に1回、そして第2引数に書いた変数のいずれかが更新された時に発火するというものらしい。
ここではレンダリング後に1回だけ動けばいいので、第2引数は空配列です。
第1引数とした関数ではvideo
タグをidで取得して、iPadの背面カメラを取得してその映像をvideoのhtml Elementに適用するだけです。(Nextの中身の内容でないのではしょり)
iPadで見ると背面カメラの映像が映ってくれた。やったね
(そのへんにあったチョコレート)
5. Vercelでデプロイしてみる
ここまでで、カメラを表示するだけのページが完成したので、どこかしかにデプロイしてみようとい流れになります。なるんです。
NextはVercelというところが作っているらしく、そのVercelは Nextをデプロイするサービスも行っているらしい。
要するに公式のどでかい波があるのでそれに乗ってみようと思う。
まずはVercelのサイトにアクセス。
おあつらえ向けにDeploy
ボタンがあるのでクリック。Githubにコードをあげているので、流れに任せてGithubでログイン。
Githubのリポジトリを取得するための権限設定を行なってリポジトリをインポート。
わたしは念の為Selected Repository(選んだリポジトリ)のみの権限から、今回作ったテストリポジトリのみの権限を与えておきました。
プロジェクト名とフレームワーク(ドロップダウンでNext.jsを指定)を設定。
root Directoryではプロジェクトフォルダ(今回ではtestappnext
)を選択してDeploy
ボタンを押してしばらく待つだけでデプロイができてしまいます。
あれ?これだけ?ってなるくらい簡単。
プロジェクト名を含むURLが払い出されるので、別端末からもアクセスできることを確認して、完成。
なんかこう、コードの方でファイルの配置どうすればいいの?とか、レンダリングがサーバーサイド??とか、レンダリング後に処理したいけどどうしたらいい??とか考えることがいっぱいだったのに、デプロイは何のよどみもなく終わってしまって、すごいなぁっておもいます。
Vercelすごーいってなったところで、この記事はこれでおしまい
Discussion