Kain's blog

Next.jsでブログを作った

Created: 2021-3-11 1:57:50

(Last updated: 2021-3-12 2:14:48)

Next.jsとtailwindcssとContentfulとVercelでブログを作ったというタイトルそのまんまの話です。

Next.jsのインストールなど

諸事情でcreate-next-appが使えなかったので npm initしてからNext.jsのインストールをしました。 自分は型付けがないと生きていけない性なので、TypeScriptとその型定義もついでにインストールします。インストール後にtsconfig.jsonを作っておくとNextの初回起動時にtsconfigの中身を埋めてくれるので空のファイルをとりあえず作る。ついでにtailwindcssやESLint、prettierなどをインストールしておく。

> npm i next react react-dom
... (中略)
> npm i -D @types/react @types/react-dom @types/node typescript next-compose-plugins
... (中略)
> touch tsconfig.json
> npm i tailwindcss
... (中略)
> npm i -D postcss autoprefixer
... (中略)

ディレクトリ構造はこんな感じにしました

.
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── next-env.d.ts
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── src
│   ├── components
│   ├── lib
│   └── pages
├── tailwind.config.js
└── tsconfig.json
  • .editorconfig, .eslintignore, .gitignore
  • package-lock.json, next-env.d.ts
    • 略その2
  • .eslintrc.js
    • ESLintの設定ファイル。settingsのreactのversionを設定しないと微妙にwarn入ったりする。
    • eslint-config-prettier使おうね
  • package.json
    • これは余談なんですがgit管理下でnpm initするとrepositoryだったりbugsだったり埋めてくれるんですね
  • postcss.config.js, tailwind.config.js
    • tailwindcssの公式にあるようにnpx tailwindcss init -pして自動生成されたファイルにpurgeを加えたのみ。
    • 注意事項はディレクトリ構成を変更しているのでsrc直下をpurgeするようにする。
  • public
    • 画像とか置く場所です
  • src/components
    • Layout.tsxとかその他もろもろのコンポーネントを置く場所
  • src/lib
    • 後述するcontentfulからデータを取ってくる用
  • src/pages
    • ページそのもの
    • getStaticPropsとかgetStaticPathsとか使いました
  • tsconfig.json
    • TypeScript用
    • Nextが自動生成するのだとstrict: falseになってたりするのでこれは修正する

Contentfulの設定など

アカウントを取得してContent modelに適当なブログのモデルを作成してフィールドを追加する。 このブログでは、title, body, slug, imagesを追加しました。(images使い方わかりませんが) bodyはRich TextではなくTextとから選ばないとMarkdown形式での入力ができない点は微妙に躓いた。

次に、npm i contentfulしてcontentfulで書いた記事をNext側から読み込む部分を作成した。

// lib/api.ts
import { createClient } from 'contentful';

const client = createClient({
  space: '***',
  accessToken: '*****',
});

interface Entry {
  body: string;
  title: string;
  slug: string;
  createdAt: string;
  updatedAt: string;
  beforeSlug?: string;
  afterSlug?: string;
}

let entries: Entry[] | undefined = undefined;

export const getPaths = async (): Promise<string[]> => {
  const entries = await client.getEntries<Entry>();
  const items: string[] = [];
  for (const item of entries.items) {
    items.push(item.fields.slug);
  }
  return items;
};

export const getEntries = async (): Promise<Entry[]> => {
  if (entries) {
    return entries;
  } else {
    const currentEntries = await client.getEntries<Entry>();
    entries = currentEntries.items.map((x) => ({
      ...x.fields,
      createdAt: x.sys.createdAt,
      updatedAt: x.sys.updatedAt,
    }));

    for (let i = 0; i < entries.length; i++) {
      const element = entries[i];
      if (i != 0) {
        element.beforeSlug = entries[i - 1].slug;
      }
      if (i != entries.length - 1) {
        element.afterSlug = entries[i + 1].slug;
      }
    }
    return entries;
  }
};

spaceとaccessTokenはContenful > Settings > API Keyから取得できるのでそれを使った。

getStaticPropsとか

すごく簡単にできて便利ですね。 注意点としてはslugとかは絶対パスで書かないとエラーが生じる。

その他

アイコンとか

アイコンに@material-ui/iconsを使ったんですが、これは単体だとデプロイできなくて@material-ui/coreもインストールする必要がある。

tailwindcssとか

CSSなんもわからんけどこれなら割と使える気がする。 ところでnext dev時にDevToolsでスタイル弄ってると重くなるのはどうしようもないんですか?

Vercelへのデプロイ

Githubからimportするだけで終わったですが、なにも書くことがない。

感想

コードハイライト入れたら露骨にLighthouseのスコア落ちたんですがこれどうにかならないんですか ビルド時にコードハイライトもマークダウンへの変換も行うようにしたら無事解決しました