Warning: count(): Parameter must be an array or an object that implements Countable in /home/xs638785/agile-software.site/public_html/wp-content/plugins/rich-table-of-content/functions.php on line 490
プロジェクト作成
npx create-next-app . --use-npm
tailwindcssの設定
tailwindcss のインストール
npm i tailwindcss
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
npx tailwindcss init -p
tailwind.config.jsの変更
'./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'
各pagesにコンポーネント
pagesの_app.jsにインポートする
import 'tailwindcss/tailwind.css'
stylesのglobals.cssにインポートする
@tailwind base;
@tailwind components;
@tailwind utilities;
Home.module.css
tailwindcssによってcssを記述するので、 Home.module.css とindex.jsに記述されているimport文を削除します。
index.js
http://localhost:3000にアクセスしたときに表示される内容を記述
ファイル作成
pages>apiにblog-page.jsとcontact-page.js
const Contact = () => {
return (
<div>
Contact page
</div>
)
}
export default Contact
const Blog = () => {
return (
<div>
Blog page
</div>
)
}
export default Blog
Layout component
comonentsフォルダを作成しLayout.jsファイル
propsで渡されたtitleの値を動的に
Headタグでは index.jsに記述されているtitle=”Home”がc
mainタグではindex.jsに記述されている<p>タグの内容がchildrenに渡されてその受け取った値を出力しています。
export
これをつけると、別のファイルから呼び出せる
children
Layoutの children
には <Layout>
と </Layout>
で囲まれた部分のコンテンツが入ってきます。
import Head from "next/head";
import Link from "next/link";
import Image from 'next/image'
export default function Layout({ children, title = "HP by Nextjs" }) {
return (
<div className="flex justify-center items-center flex-col min-h-screen text-gray-600 text-sm font-mono">
<Head>
<title>{title}</title>
</Head>
<header>
<nav className="bg-gray-800 w-screen">
<div className="flex items-center pl-8 h-14">
<div className="flex space-x-4">
<Link href="/">
<a className="text-gray-300 hover:bg-gray-700 px-3 py-2 rounded">
Home
</a>
</Link>
<Link href="/blog-page">
<a className="text-gray-300 hover:bg-gray-700 px-3 py-2 rounded">
Blog
</a>
</Link>
<Link href="/contact-page">
<a className="text-gray-300 hover:bg-gray-700 px-3 py-2 rounded">
Contact
</a>
</Link>
</div>
</div>
</nav>
</header>
<main className="flex flex-1 justify-center items-center flex-col w-screen">
{children}
</main>
<footer className="w-full h-12 flex justify-center items-center border-t">
<a
className="flex items-center"
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
{/* <img src="/vercel.svg" alt="Vercel Logo" className="h-4 ml-2" /> */}
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</a>
</footer>
</div>
);
}
index.js
作成したLayout.jsをindex.jsに適用させるためにimportします。
import Layout from "../components/Layout";
export default function Home() {
return (
<Layout title="Home">
<p className="text-4xl">Welcome to Nextjs</p>
</Layout>
);
}
Blog page
import Layout from "../components/Layout";
import Post from "../components/Post";
import { getAllPostsData } from "../lib/posts";
export default function Blog({ posts }) {
return (
<Layout title="Blog">
<ul className="m-10">
{posts && posts.map((post) => <Post key={post.id} post={post} />)}
</ul>
</Layout>
);
}
export async function getStaticProps() {
const posts = await getAllPostsData();
return {
props: { posts },
};
}
Contact page
publicフォルダーにsvgファイルなどを格納
Imageコンポーネント自動的に画像の圧縮
import Layout from "../components/Layout";
import Image from "next/image";
export default function Contact() {
return (
<Layout title="Contact">
<div className="bg-white text-center shadow-xl p-8 w-80 rounded">
<div className="mt-4">
<p className="font-bold">Contant info</p>
</div>
<div className="flex justify-center mt-4">
<Image
className="rounded-full"
src="/avatar.jpg"
width={60}
height={60}
alt="Avatar"
/>
</div>
<div className="mt-4">
<p className="font-bold">Address</p>
<p className="text-xs mt-2 text-gray-600">city A</p>
<p className="font-bold mt-3">E-mail</p>
<p className="text-xs mt-2 text-gray-600">abc@gmail.com</p>
<p className="font-bold mt-3">Phone</p>
<p className="text-xs mt-2 text-gray-600">000-123-456</p>
</div>
<div className="mt-6 flex justify-around">
<div>
<a
href="https://nerdcave.com/tailwind-cheat-sheet"
target="_blank"
rel="noopener noreferrer"
>
<svg
className="w-6 h-6 mr-3 text-blue-500"
stroke="currentColor"
fill="currentColor"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path>
</svg>
</a>
</div>
<div>
<a
href="https://nerdcave.com/tailwind-cheat-sheet"
target="_blank"
rel="noopener noreferrer"
>
<svg
className="w-6 h-6 mr-3 text-blue-700"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z" />
</svg>
</a>
</div>
<div>
<a
href="https://nerdcave.com/tailwind-cheat-sheet"
target="_blank"
rel="noopener noreferrer"
>
<svg
className="w-6 h-6 mr-3 text-gray-500"
fill="currentColor"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path>
</svg>
</a>
</div>
</div>
</div>
</Layout>
);
}
getStaticProps
npm run dev:リクエストごとに実行される
npm start:ビルド時に実行される
jsonの形式でpostsに格納
lib>posts.js
- getAllPostIds():idの一覧を取得
- getPostData:特定のidを使ってデータベースからビルド時にデータを取得する
import fetch from "node-fetch";
const apiUrl = "https://jsonplaceholder.typicode.com/posts";
export async function getAllPostsData() {
const res = await fetch(new URL(apiUrl));
const posts = await res.json();
return posts;
}
export async function getAllPostIds() {
const res = await fetch(new URL(apiUrl));
const posts = await res.json();
return posts.map((post) => {
return {
params: {
id: String(post.id),
},
};
});
}
export async function getPostData(id) {
const res = await fetch(new URL(`${apiUrl}/${id}/`));
const post = await res.json();
// return {
// post,
// };
return post;
}
Post.js
ブログの一覧から詳細に飛ぶためのリンクを作成。
import Link from "next/link";
export default function Post({ post }) {
return (
<div>
<span>{post.id}</span>
{" : "}
<Link href={`/posts/${post.id}`}>
<span className="cursor-pointer text-blue-500 border-b border-blue-500 hover:bg-gray-200">
{post.title}
</span>
</Link>
</div>
);
}
Dynamic routes
- getStaticPath()
pages>postsのフォルダを作成しそのフォルダ内に[id].jsファイルを作成
posts>[id].js
import Link from "next/link";
import Layout from "../../components/Layout";
import { getAllPostIds, getPostData } from "../../lib/posts";
export default function Post({ post }) {
if (!post) {
return <div>Loading...</div>;
}
return (
<Layout title={post.title}>
<p className="m-4">
{"ID : "}
{post.id}
</p>
<p className="mb-8 text-xl font-bold">{post.title}</p>
<p className="px-10">{post.body}</p>
<Link href="/blog-page">
<div className="flex cursor-pointer mt-12">
<svg
className="w-6 h-6 mr-3"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M15.707 15.707a1 1 0 01-1.414 0l-5-5a1 1 0 010-1.414l5-5a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 010 1.414zm-6 0a1 1 0 01-1.414 0l-5-5a1 1 0 010-1.414l5-5a1 1 0 011.414 1.414L5.414 10l4.293 4.293a1 1 0 010 1.414z"
clipRule="evenodd"
></path>
</svg>
<span>Back to blog-page</span>
</div>
</Link>
</Layout>
);
}
export async function getStaticPaths() {
const paths = await getAllPostIds();
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
//const { post: post } = await getPostData(params.id);
const post = await getPostData(params.id);
return {
props: {
post,
},
};
}