Produced by FOURIER

Nuxt.jsのライフサイクルでハマった話

IchikawaIchikawa calender 2022.7.26

こんにちは。テックブログ副編集長 (自称) の市川です。
副編集長といってもやってることは社内メンバーが書いた記事をアップするだけですが...
ではなぜ副編集長を自称しているかと言うと、このテックブログを作ったからです。
ということで、今回はテックブログで使っているNuxt.jsのネタを書いていきます。

ちなみに、なぜNuxt.jsを選択したかというと、
nuxt/contentというマークダウンファイルから記事を生成してくれる便利なモジュールがあったからです。エンジニアにとってはマークダウンだと書きやすいですよね!
他にも全文検索・カテゴリ検索等もできますし、プラグインを入れることで目次の自動生成をやってくれたりとイイ感じです。

Content made easy for Vue developers · Nuxt Content

The file-based CMS for your Nuxt application, powered by Markdown and Vue components.

https://content.nuxtjs.org/

話がそれましたね...

それではNuxt.jsSSRSSGにおけるライフサイクルの違いによってハマった話どうぞ!

発生した不具合

まずはどんな不具合が発生したかというと、 ページ右上にある検索機能が正しく動作しませんでした。

キーワードを入れて検索してもフィルタリングされないで全件とってきてしまう…

しかもローカル環境では正しく動作するのに、本番環境では動かない!なぜ!?

ハマった理由としては、

開発モード(SSR) と 本番環境(SSG)におけるライフサイクルの違いが関係してきます。

Nuxt.jsのビルド

SSRSSGというキーワードがでてきたので簡単に説明しますね。

Nuxt.jsのビルド方法にはSPASSRSSGの三つがあり、それぞれの特徴として以下のようになってます。

SPA (Single Page Application)

初回リクエストでページを取得し、2回目以降はAPIからデータを受け取ってコンテンツを置き換える。初回のレンダリンングが遅くなりやすいが、2回目以降のページ遷移はスムーズに動作します。

クローラーがJavaScriptを実行できない?とSEOで不利になると言われています。

nuxt.config.js の設定方法は以下の通り。

export default {
  ssr: false,
  target: 'static',
}

SSR (Server Side Rendering)

SPAではブラウザ側で行っていたレンダリング処理を、サーバー側で行うのでブラウザのスペックの高くない機器(スマホなど)でも安定した表示速度を保てます。

代わりにサーバー側でJavaScriptを実行できる環境(Node.js)が必要になります。

nuxt.config.js の設定方法は以下の通り。

export default {
  ssr: true,
  target: 'server',
}

SSG (Static Site Generator)

事前に静的ファイル(HTML)を生成してサーバーにおいて置きます。

動的なコンテンツや更新頻度が少ない場合に使います。まさにブログ!

額に動的なコンテンツが多いとクライアントに負担がかかり動作が遅くなったり、更新頻度が多いと何度もビルド対応しないといけないので大変です。

nuxt.config.js の設定方法は以下の通り。

export default {
  ssr: false,
  target: 'static',
}

ライフサイクル

Nuxt.jsがページを表示する際に決められた順にライフサイクルフック(関数)を呼び出していきます。

ここでポイントになるのがSPASSRSSGでライフサイクルフックの呼ばれるタイミングがビミョーに違うということです。

記事検索の処理ですが、nuxt/contentのドキュメントを参考に以下のようなコードを書いてました。

async asyncData ({ $content, route}: Context) {
    const articles = await new Promise((resolve) => {
      const keyword = route.query.keyword
      let query = $content('articles')
      if (keyword !== null) {
        query = query.search(keyword)
      }
      resolve(query.sortBy('published', 'desc').fetch())
    });
    return { articles }
  }

const keyword = route.query.keyword でパラメータに含まれる検索ワードを取得して、検索ワードが存在すればフィルタリングをかけて記事を取得してくれます。

ページ遷移時の挙動としてSSRではasyncDataが呼ばれるのに、SSGでは呼ばれません。

そこでどうなるかというと

generateした際にasyncData が呼ばれる(サーバーサイドの処理)

 ↓

generateの実行タイミングではroute.query.keywordの中身はからなので、全件検索が実行される

 ↓

クライアント(ブラウザ)でパラメータに検索キーワードを持たせてページ遷移してもasyncData が呼ばれないので、generate時の実行結果(記事全件)が表示される

 ↓

検索機能動かない!!となるわけです。

最後に

SPASSRSSGとライフサイクルを理解するとNuxt.jsと仲良くなれるかと思います。

僕はここの違いを把握するまでけっこう時間かかりました...

やっぱりドキュメンは見よう!

新しいメンバーを募集しています

Ichikawa

Ichikawa / Engineer

パン屋から転身してエンジニア3年目。主にPHP/Laravelを使っています。最近ではVue.js/Nuxt.jsと人間に興味あり。