このブログをNuxtJSのSSGでリプレイス

2021-02-18


Nuxt or Next + contenful + Netlify or Vercelの構成でブログ作成した記事はたくさんあるので基本それ参考にしてます。

Vue CLiで動いてたものをNuxtのSSGを使いながら動くようにしただけのため、きっとNuxtの機能を効率的に使えてるわけではない気がしてる。

リポジトリ↓
https://github.com/shira79/MyBlog

過去のもの

Vue CLiからVue Routerを用いて、SPAにしてた。ブログのデータはContentfulにあって、リクエストがあるたびに毎回apiを叩いていた。

Nuxtに置き換えた理由はSSG

SSGを使いたかったのが最大の理由です。
使いたかった理由は以下です。

速度

SPAだとブラウザ上でjsを実行する時間がかかり、初回ローディングが遅いのが気になっていました。SSGの機能を使って、あらかじめSSGを使ってHTMLを生成しておけば早いです。
また、毎度ページ遷移するごとにContentfulのAPIを叩くのですが、それもなくなってます。

シークレットモードで開いたら試せるかと思います。

OGPを設定したい

OGPの例は↓です。

OGPとは、「Open Graph Protcol」の略でFacebookやTwitterなどのSNSでシェアした際に、設定したWEBページのタイトルやイメージ画像、詳細などを正しく伝えるためのHTML要素

SPAだと、SEO関連のmetaをどーやって設定するか問題が発生します。

クローラーによって、javascriptに対応してるもの、対応してないものがあるようです。javascriptに対応していないクローラーでは、OGPが適切に読みこまれなくなってしまいます。

解決手段は他にもいろいろあるようですが、SSGであらかじめjavascriptを反映させたHTMLを配置しておけば解決します。

使用技術

構成


Netlify、Contentfulは以前の環境と変わらずです。

変更点は以下です。

  • nuxtで静的ページ生成してる
  • webhookを登録してため、Contentfulに変更があるとnetlifyのビルドコマンドが動く
  • OGP画像配信にCloudinaryを使用

NuxtJS

NuxtでSSGを実現するためには、主に以下へ処理を記述します。

//各pageコンポーネントでのasyncDataフックのサンプル
async asyncData({params,route}){

      const BlogEntry = await ContentfulAdapter.getEntryById(params.id)
      const SocialLinksEntry = await ContentfulAdapter.getSocialLinks()

      return {
          meta  :{
              title: BlogEntry.fields.title,
              description: BlogEntry.fields.text,
              path :route.fullPath,
          },
          blog : BlogEntry,
          links : SocialLinksEntry.items,
      }
  },
//nuxt.config.jsのgenerateプロパティ
generate: {
    fallback: true,
    async routes() {

      var ret = [];

      ret.push('/');
      ret.push(`/about`);

      const allBlogs = await ContentfulAdapter.getAllBlogs()

      //blog/_id
      await Promise.all(allBlogs.items.map(function(blog) {
          ret.push(`/blogs/${blog.sys.id}`);
      }))

      let blogLastPage = ContentfulAdapter.getLastPage(allBlogs.total);
       //blog/list/_page
      await Promise.all([...Array(blogLastPage).keys()].map(function(page) {
          if(page == 0){
              ret.push( `/blogs/list`);
          }else{
              ret.push(`/blogs/list/${page+1}`);
          }
      }))

      const tagList = await ContentfulAdapter.getAllTags();
      //tag/_enName/_page
      await Promise.all(tagList.items.map(async function(tag){
          let blogs = await ContentfulAdapter.getPaginatedBlogsByTagId(tag.sys.id);
          let tagLastPage = ContentfulAdapter.getLastPage(blogs.total);
          [...Array(tagLastPage).keys()].map(function(page) {
              if(page == 0){
                  ret.push( `/tags/${tag.fields.enName}`);
              }else{
                  ret.push( `/tags/${tag.fields.enName}/${page+1}`);
              }
          })
      }))

      return ret;
    }
  },

上記のgenerateの記述では、このサイトマップに書かれてるようなURLを返却してます

以上は自分の書いたコードを貼り付けましたが、neflifyの公式ブログでnuxt+contentful構成のサンプルコードがあるので、僕のよりそっちみた方がいいかもです。

てか初めっからこの記事を紹介したら事足りるのかもしれません

contentful

https://www.contentful.com/pricing/
前からですが、無料プランを使ってます。コンテンツの数が5000以内じゃないと死にます。

webhookの設定はたくさん記事があるので、↓みたいなの参考にしてます
Netlify Build hooks + Contentful Webhookで記事公開フローを効率化する

cloudinary

OGP画像の配置にはcloudinaryをというストレージサービスを使用してます。

以下2つをみてやり方を知りました。面白いので見てほしいです。

CloudinaryはURLにパラメータを設定すること動的に画像に変更を加えられます。
他にもたくさん機能があるみたいですが、僕はこの機能しか知りません。

Cloudinaryに以下のようなベース画像を配置しています。

https://res.cloudinary.com/shlia34-com/image/upload/title_zm6zse.jpg

URLにパラメーターをつけます。今回は、テキストのフォント、文字列、幅などです。
このブログでは、実際に以下のような画像URLをOGP画像として設定していますが、これだけでQiitaやZennと似たようなことができます。
↓実際に僕は以下のようなURLにしてます。
https://res.cloudinary.com/shlia34-com/image/upload/l_text:Sawarabi Gothic_50_bold:ここはタイトル,w_450,c_fit/v1610548616/title_zm6zse.jpg

クエリ変えると画像も変わるのでドキュメントのここらへんみたら遊べると思います。
結構感動しました。

感想

  • これと同じ構成で作られてるブログを見たときに、見た目綺麗なのが羨ましくなってきた。けどデザインもcssのお勉強をするのは、重たそうで腰が重い、、。
  • いくつかのサービスやライブラリを組み合わせて作ったけど、それらを生み出した人達 is めちゃくちゃかっこいい
  • なんだかんだブログ作り長いことやったけど、ちょっともう飽きてきてるので別の勉強して中身を分厚くしていければいいな
  • contentfulと同じヘッドレスCMSとしてmicroCMSという日本製のサービスがあって、公式の日本語記事が充実してるのでこっちの方が楽だったのでは??と思いはじめてる。