概要
業務でNext.jsを触る機会を得たため、まずは下記チュートリアルをやってみました。
チュートリアルの章ごとの備忘録をまとめます。
https://nextjs.org/learn/basics/getting-started
Getting Started
Introduction
Next.js良いぞ!と機能紹介しているページ。一旦読み飛ばす
Setup
作業用のhello-next
ディレクトリを作成し、以降ここで作業。
まずは、npmコマンドで必要なライブラリをインストールし、
ビュー用のファイルを置くことになるpagesディレクトリを作成します。
$ mkdir hello-next
$ cd hello-next
$ npm init -y
$ npm install --save react react-dom next
$ mkdir pages
- 作業後のディレクトリ構成
$ ll
drwxr-xr-x 524 aota staff 16K 12 9 21:00 node_modules/
-rw-r--r-- 1 aota staff 263K 12 9 21:00 package-lock.json
-rw-r--r-- 1 aota staff 324B 12 9 21:00 package.json
drwxr-xr-x 2 aota staff 64B 12 9 21:08 pages/
- package.jsonの中身
$ cat package.json
{
"name": "hello-next",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^9.1.4",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
}
上記package.jsonのscriptsを下記に書き換えるよう指示されるので置換する。
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
- 置換後のpackage.jsonの中身
$ cat package.json
{
"name": "hello-next",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^9.1.4",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
}
あとは、下記コマンド実行で準備完了!
$ npm run dev
ブラウザでhttp://localhost:3000
にアクセスしてみる
何が表示された?と聞かれるので、404だよーと選択し次へ。
404 Page
404表示されてるかい?それでOKだ
Creating Our First Page
先程作ったpagesディレクトリ配下にindex.jsを作る
$ vi pages/index.js
- pages/index.jsの中身
const Index = () => (
<div>
<p>Hello Next.js</p>
</div>
);
export default Index;
ブラウザで先程開いていたhttp://localhost:3000
をリロードしてみる
Hello Next.jsと表示されてるのでOK!
ここで、シンタックスエラーを起こしてみよう。
タグの閉じタグを消してみる
- pages/index.jsの中身
const Index = () => (
<div>
<p>Hello Next.js
</div>
);
export default Index;
ブラウザで再度http://localhost:3000
を開いてみる
何が表示された?と聞かれるので、Syntaxエラーがでたよと選択し次へ
Handling Errors
Next.jsではデフォルトでブラウザにエラーを出力するよ。
エラーを直したらリロードしなくてもページが再レンダリングされるんだ。
You are Awesome
ファーストステップはこれでおしまい!
Navigate Between Pages
単体のページは、できたので今度は複数のページを作ろう。
先程作ったpagesディレクトリ配下にabout.jsを作る
$ vi pages/about.js
- pages/about.jsの中身
export default function About() {
return (
<div>
<p>This is the about page</p>
</div>
);
}
ブラウザでhttp://localhost:3000/about
にアクセスしてみる
うむ、ちゃんと表示された。
次は、これらのページをつなげてみる
ただ<a>
タグで書いちゃうと、サーバーとの通信が発生するよね。
クライアントサイドでナビゲーションしたいから、<Link>
タグを使うよ。
使い方は次回。
Using Link
作っていたpages
配下のindex.js
を<Link>
タグを使うように書き換える。
$ vi pages/index.js
- pages/index.jsの中身
// This is the Link API
import Link from 'next/link';
const Index = () => (
<div>
<Link href="/about">
<a>About Page</a>
</Link>
<p>Hello Next.js</p>
</div>
);
export default Index;
ブラウザでhttp://localhost:3000/about
にアクセスしてみる
About Page
をクリックしてaboutページに遷移後、ブラウザの戻るボタンで戻っても
正常にindexページに遷移することが確認できる。
(サーバーにリクエストがいっていないのは、Chromeの開発者モードのNetworkタブから確認できる)
Client-Side History Support
先程、ブラウザの戻るボタンで戻れたのは、
location.history
を<Link>
タグがハンドリングしてたからなんだ。
Adding Link Props
属性値を追加したいときあるよね。
title属性を追加したい場合は、こうするはずだ。
- pages/index.jsの中身
// This is the Link API
import Link from 'next/link';
const Index = () => (
<div>
<Link href="/about">
<a title="About Page">About Page</a>
</Link>
<p>Hello Next.js</p>
</div>
);
export default Index;
これは正常にレンダリングされる。では下記の場合はどうか。
- pages/index.jsの中身
// This is the Link API
import Link from 'next/link';
const Index = () => (
<div>
<Link href="/about" title="About Page">
<a>About Page</a>
</Link>
<p>Hello Next.js</p>
</div>
);
export default Index;
ブラウザでhttp://localhost:3000/about
にアクセスしてみる
Chromeの開発者モードのElementsタブを確認してもtitle属性は、<a>
タグに追加されていない。
また、Chromeの開発者モードのConsoleタブを確認するとエラーがでている。
何が表示された?と聞かれるので、title属性はないしConsoleでエラーがでたよと選択し次へ
Link is Just a Higher Order Component (HOC)
実際、title属性を追加しても<Link>
タグには、なんの影響もないんだ。
なぜなら、hrefか近い属性しか<Link>
タグは許容していないからだ。
(<Link>
タグもといnext/link
がHOCというもので、通常のコンポーネントではないということ。)
属性を追加したい場合は、その下のコンポーネント、今回であれば<a>
タグに渡せば良い。
また<Link>
タグの子コンポーネントとして必要なことは、onClick属性が使えることだけだ。
Link is Simple, but Powerful
これまで基本的な<Link>
タグの使い方をみてきたけど、
今後のレッスンではもっと面白い使い方を紹介するよ。
それまでは、Next.js Routing documentationをみてみよう。
Using Shared Components
Introduction
共通のHeaderコンポーネントを作って、複数のページで読み込んでみよう
Create the Header Component
componentsディレクトリを作成し、配下にHeader.jsを作成する。
$ mkdir components
$ vi components/Header.js
- components/Header.jsの中身
import Link from 'next/link';
const linkStyle = {
marginRight: 15
};
const Header = () => (
<div>
<Link href="/">
<a style={linkStyle}>Home</a>
</Link>
<Link href="/about">
<a style={linkStyle}>About</a>
</Link>
</div>
);
export default Header;
Using the Header Component
pages/index.jsとpages/about.jsを編集し、先程作成したHeaderコンポーネントを読みこむ。
- pages/index.jsの中身
import Header from '../components/Header';
export default function Index() {
return (
<div>
<Header />
<p>Hello Next.js</p>
</div>
);
}
- pages/about.jsの中身
import Header from '../components/Header';
export default function About() {
return (
<div>
<Header />
<p>This is the about page</p>
</div>
);
}
ブラウザでhttp://localhost:3000
にアクセスしてみる
Headerコンポーネントが読み込まれ、Home、Aboutそれぞれで遷移することが確認できる。
ここでちょっとした変更をしてみよう。
-
$ npm run dev
で起動したアプリケーションをSTOPする。(Ctrl
+C
) - componentsディレクトリをcompsにリネームしてみる。
- pages/index.jsおよびpages/about.jsで、Headerコンポーネントの読み込み先を
../comps/Header
に変更する。 - アプリケーションを再起動する(
$ npm run dev
)
- pages/index.jsの中身
import Header from '../comps/Header';
export default function Index() {
return (
<div>
<Header />
<p>Hello Next.js</p>
</div>
);
}
- pages/about.jsの中身
import Header from '../comps/Header';
export default function About() {
return (
<div>
<Header />
<p>This is the about page</p>
</div>
);
}
何が表示された?と聞かれるので、問題なく動いたと選択し次へ
The Component Directory
ちゃんと動くよね。コンポーネントを置くディレクトリは好きな名前にしていいんだ。
特別な名前が必要なのは、/pages
ディレクトリと、/public
ディレクトリだけなんだ。
もちろん、コンポーネントは/pages
ディレクトリ配下にも作成できる。
ただ今回そうしなかったのは、Headerコンポーネントへの直接のリンクが不要だからだ。
(http://localhost:3000/Header
にアクセスするのは不要。)
※ ここでcompsは、componentsにリネームして戻しておく。アプリケーションの再起動も忘れずに
The Layout Component
共通のCSSスタイルをあてたいときあるよね。
共通のレイアウトコンポーネントを作っていくよ。
まず、componentsディレクトリ配下にMyLayout.jsを作成するよ。
- components/MyLayout.jsの中身
import Header from './Header';
const layoutStyle = {
margin: 20,
padding: 20,
border: '1px solid #DDD'
};
const Layout = props => (
<div style={layoutStyle}>
<Header />
{props.children}
</div>
);
export default Layout;
そして、このレイアウトコンポーネントを使うためにpages/index.jsとpages/about.jsを編集する。
- pages/index.jsの中身
import Layout from '../components/MyLayout';
export default function Index() {
return (
<Layout>
<p>Hello Next.js</p>
</Layout>
);
}
- pages/about.jsの中身
import Layout from '../components/MyLayout';
export default function About() {
return (
<Layout>
<p>This is the about page</p>
</Layout>
);
}
ブラウザでhttp://localhost:3000/about
にアクセスしてみる
レイアウトコンポーネントで指定したborder: '1px solid #DDD'
の枠線があることが確認できた。
次に、components/MyLayout.jsから{props.children}
を削除してみよう。
- components/MyLayout.jsの中身
import Header from './Header';
const layoutStyle = {
margin: 20,
padding: 20,
border: '1px solid #DDD'
};
const Layout = props => (
<div style={layoutStyle}>
<Header />
</div>
);
export default Layout;
ブラウザでhttp://localhost:3000/about
にアクセスしてみる
何が表示された?と聞かれるので、コンテンツが消えたと選択し次へ
Rendering Child Components
{props.children}
を削除すると、Layoutコンポーネントの中においたコンテンツはレンダリングされないんだ。
Layoutコンポーネントの作成方法をひとつ紹介したけど、他にも作成する方法があるよ。
Method 1 - Layout as a Higher Order Component
// components/MyLayout.js
import Header from './Header';
const layoutStyle = {
margin: 20,
padding: 20,
border: '1px solid #DDD'
};
const withLayout = Page => {
return () => (
<div style={layoutStyle}>
<Header />
<Page />
</div>
);
};
export default withLayout;
// pages/index.js
import withLayout from '../components/MyLayout';
const Page = () => <p>Hello Next.js</p>;
export default withLayout(Page);
// pages/about.js
import withLayout from '../components/MyLayout';
const Page = () => <p>This is the about page</p>;
export default withLayout(Page);
Method 2 - Page content as a prop
// components/MyLayout.js
import Header from './Header';
const layoutStyle = {
margin: 20,
padding: 20,
border: '1px solid #DDD'
};
const Layout = props => (
<div style={layoutStyle}>
<Header />
{props.content}
</div>
);
export default Layout;
// pages/index.js
import Layout from '../components/MyLayout.js';
const indexPageContent = <p>Hello Next.js</p>;
export default function Index() {
return <Layout content={indexPageContent} />;
}
// pages/about.js
import Layout from '../components/MyLayout.js';
const aboutPageContent = <p>This is the about page</p>;
export default function About() {
return <Layout content={aboutPageContent} />;
}
Using Components
今回は、2つの方法について紹介したよ。
- 共通のHeaderコンポーネントとして
- Layoutコンポーネントとして
コンポーネントは、スタイル適用やページのLayoutなどに使えるんだ。
また、NPMモジュールから読み込む方法もあるよ。