LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

バックエンドが要らない手軽なサイトをNext.jsで作ってみた【Next.js活用編】

この記事はLCL Advent Calendar 2020 - 22日目の記事です。

qiita.com

過去2回の記事の続きを進めていきます。
まだ読まれてない方はこちらから先にご覧ください。

techblog.lclco.com

techblog.lclco.com

さて、ここまで無事にサイトを公開することができました。

https://remote-sale.vercel.app/

しかし、このままではNext.jsの良いところをあまり学べてない気がしますね。
もう少し現実的な実装をしていこうと思います。

試してみるのはSSG(Static Site Generation)と画像最適化。どちらも少し生きたサイトを公開する上で必要な機能になるので今回はこれらを進めていきます。

SSG(Static Site Generation)

現時点ではアクセスする度にJSONを読み込んでHTMLを都度生成しているのでまずSSGに対応します。

nextjs.org

GetStaticPropsを使うことでビルド時にデータを取得してHTMLを生成するようになるため、サイトが表示される速度は高速になります。 このサイトは動的な要素はないのでデータが更新されるビルド時のみにデータを取得してあとは静的生成されたHTMLを表示するこの方法が最適ですね。

import { NextPage, GetStaticProps } from 'next'

...

type StaticProps = {
  saleSections: SaleSection[],
  updatedAt: Date
}

const Index: NextPage<StaticProps> = ({ saleSections, updatedAt }) => {
  ...
}
 
export const getStaticProps: GetStaticProps = async () => {
  const saleSections = require('../../.contents/saleSections_copy.json');
  const today = (new Date()).toLocaleString();
  return {
    props: {
      saleSections: saleSections,
      updatedAt: today
    }
  }
}

ざっくり実装を追ってみましょう。まずgetStaticPropsでデータを取得するようにしました。引数等も調整していますが、大きな変更はこれだけです。 これまでIndex内で生成していた更新日時もこれにより、ちゃんと最後に更新された日時を表示するようになります。

ちなみにIndex内で都度生成していた際はJSTでしたが、getStaticPropsではUTCになっていました。Vercelのビルド環境が影響していると思いますが、この辺は開発する上で注意が必要そうです。

※アフィリエイト成果がほぼ無いためか、Amazon APIの取得上限に達ししてしまったため手入力したコピーデータを利用しています。

Before After
f:id:yamshta:20201221232331p:plain f:id:yamshta:20201221232357p:plain

折角なのでPageSpeed Insightsを確認してみます。途中コピーデータになってしまいましたがデータ量はほぼ同じの状態でこれだけ変化がありました。

画像圧縮

PageSpeed Insightsでスコアが改善したので序でに画像も最適化しましょう。
Next.js 10で公開された自動で画像的に最適化してくれる next/image を使います。

nextjs.org

nextjs.org

さっそく前回作った Section.tsx に実装していきます。 といっても、next/imageを読み込んでImageコンポーネントを使うだけです。Imageコンポーネントは同名でChakra UIで使っていたのでそのままで問題ありませんが、Chakra UIの読み込み部分は消しましょう。

...
import { Wrap, WrapItem, Box, Text, Link, Badge, useColorMode } from '@chakra-ui/react';
import Image from 'next/image'

...

const Item = ({ props }: { props: SaleItem }) => {
  const { title, detailPageURL, imageURL, amount, saving } = props;
  const { colorMode } = useColorMode()
  const bgColor = { light: 'white', dark: 'gray.800' }
  return (
    <WrapItem>
      <Link href={detailPageURL} isExternal>
        <Box
            bg={bgColor[colorMode]}
            boxShadow="lg"
            w="200px"
            minW="200px"
            rounded="md"
            >
          <Box>
            <Badge
              position="absolute"
              fontStyle="italic"
              backgroundColor="#f00"
              color="white"
              my={4}
            >
                {saving}
            </Badge>
            <Image
              src={imageURL}
              width="100%"
              height="200px"
              objectFit="contain"
              p={6}
            />

外部ドメインの画像ファイルの場合は next.conig.js にドメインを設定する必要があります。

module.exports = {
  images: {
    domains: ['m.media-amazon.com'],
  },
}

これだけでNext.jsがイイ感じに最適化してくれます。サイトを表示するとこんな感じ。

f:id:yamshta:20201221232429p:plain

修飾されていたChakra UIのコンポーネントをそのまま上書きしてしまったので少しズレてますが、これでPageSpeed Insightsを計ってみると...

f:id:yamshta:20201221232536p:plain

無事に90点台になりました👏 PCも99点です🎉

f:id:yamshta:20201221232507p:plain

以上!

本当はOGPやメタタグの追加、PWAの対応、カスタムドメインの設定(vercel)なども予定していたのですが、作業時間が取れなかったため、短いですがここで一区切りとさせていただきます。

簡単なサイトでしたが、サクサクと実装できるNext.jsはとても🆒な技術だと思いました。 私が業務で使うことは今のところないですが、フロントエンドメンバーの作業を覗きつつ学習していきたいと思います。

そしてバックエンドの構築が今よりもラクにできるようになったらまた違ったサイトをブログネタで作ってみたいと思います。