Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Nuxt.js のインスタンスライフサイクル総点検

Nuxt.js のインスタンスライフサイクル総点検

2021年前半に作成し、発表の場を失ったスライドです。
Nuxt2のライフサイクルについて触れており、Nuxt3のライフサイクルについては感知していません。
また、Nuxt3に関する情報についても一部古いものがあります。

Takuya Eguchi (Akagire)

March 22, 2022
Tweet

More Decks by Takuya Eguchi (Akagire)

Other Decks in Technology

Transcript

  1. https://github.com/vuejs/vue/blob/master/dist/vue.js#L4430 https://jp.vuejs.org/v2/api/index.html#beforeCreate ◆ 「データの監視(より前)」とは ◆ data へのアクセスができないという意味 ◇ prefetch 用途には使えない

    ◆ 「イベント・ウォッチャのセットアップより前」 ◆ コンポーネントのライフサイクル設定前という意味 ◇ ライブラリ作成者とかでない限り意識しなくていい beforeCreate 関数 8 “データの監視とイベント/ウォッチャのセットアップより前の 
 インスタンスが初期化されるときに同期的に呼ばれます。” 

  2. https://github.com/vuejs/vue/blob/master/dist/vue.js#L4434 https://jp.vuejs.org/v2/api/index.html#created ◆ 「マウンティングの段階は未開始で〜」 ◆ $el とは? ◇ Vue.js をマウントする物理

    DOM ◆ DOM に触れないという理解で OK ◇ 具体的に document とか window に触れない created 関数 9 “インスタンスが作成された後に同期的に呼ばれます。この段階では、インスタン スは、データ監視、算出プロパティ、メソッド、watch/event コールバックらのオプ ションのセットアップ処理が完了したことを意味します。しかしながら、マウンティ ングの段階は未開始で、$el プロパティはまだ利用できません。”

  3. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2638 https://jp.vuejs.org/v2/api/index.html#beforeMount ◆ 「マウンティングが開始される直前に呼ばれ〜」 ◆ created と同じで DOM に触れないという理解でOK ◆

    「SSR では呼ばれません」 ◆ これ以降のライフサイクルがクライアントサイドのみで実行 されるという理解でOK(もうちょっと先で解説します) ◆ 以降の hook も SSR 非対応なので記載省略してます beforeMount 関数 ※CSRのみ
 10 “ render 関数が初めて呼び出されようと、
 マウンティングが開始される直前に呼ばれます。
 
 このフックはサーバサイドレンダリングでは呼ばれません。”

  4. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2672 https://jp.vuejs.org/v2/api/index.html#mounted ◆ 「インスタンスがマウントされたちょうど後に呼ばれます」 ◆ DOM にアクセスできるようになる、という理解でOK ◆ 「vm.$nextTick を使うことができます」

    ◆ mounted はノンブロッキングで実行されている ◆ 以降の hook もノンブロッキングなので記載省略してます mounted 関数 ※CSRのみ ※ノンブロッキング 11 “新たに作成される vm.$el によって置き換えられる el に対して、インスタンスがマウントされたちょ うど後に呼ばれます。ルートインスタンスがドキュメントの中の要素にマウントされる場合、 vm.$el も mounted が呼び出されるときにドキュメントの中に入ります。 
 mounted は 全ての子コンポーネントもマウントされていることを保証しないことに注意してくださ い。ビュー全体がレンダリングされるまで待つ場合は、 mounted の代わりに vm.$nextTick を使 うことができます。”

  5. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2522 https://jp.vuejs.org/v2/api/index.html#beforeUpdate ◆ 「更新前に既存の DOM にアクセスするのに適しています」 ◆ そうだね beforeUpdate 関数

    ※CSRのみ ※ノンブロッキング 12 “データが変更されるとき、DOM が適用される前に呼ばれます。これは、更新前 に既存の DOM にアクセスするのに適しています。例: 手動で追加されたイベント リスナを削除する”

  6. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2888 https://jp.vuejs.org/v2/api/index.html#updated ◆ 「このフックでは状態を変更するのを回避すべきです」 ◆ 自コンポーネントに対する状態変化を伴わない処理のみに使う ◇ eg. API を叩く,

    イベントリスナーを貼り直す updated 関数 ※CSRのみ ※ノンブロッキング 13 “データが変更後、仮想 DOM が再描画そしてパッチを適用によって呼ばれます。 
 
 このフックが呼び出されるとき、コンポーネントの DOM は更新した状態になり、このフックで DOM に依存する操作を行うことができます。しかしがながら、ほとんどの場合、無限更新ループに陥る可 能性があるため、このフックでは状態を変更するのを回避すべきです。” 

  7. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2763 https://jp.vuejs.org/v2/api/index.html#activated https://github.com/vuejs/vue/blob/master/dist/vue.js#L2779 https://jp.vuejs.org/v2/api/index.html#deactivated ◆ 特殊なユースケースの hook…? ◆ 3年Vue.js書いてきて使ったことなし ◆

    右記のコードを書いたとき、 動的に変化するコンポーネントの 状態を保持できる ◆ :selectedComponentName コンポーネント の状態変化時に hook される関数 activated 関数, deactivated 関数 ※CSRのみ 14 “生き続けたコンポーネントが活性化するとき呼ばれます。” 
 “生存し続けたコンポーネントが非活性化されるとき呼ばれます。” 

  8. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2573 https://jp.vuejs.org/v2/api/index.html#beforeDestroy ◆ 「Vue インスタンスが破棄される」 ◆ Routerでページ離脱が一番ある ◆ 単一ページでよく起きるのは →のコードで

    activate が false になったとき ※import と component を省略しちゃった ◆ a タグや location.href で移動した ときはこのイベントを介さない beforeDestroy 関数 ※CSRのみ 15 “Vue インスタンスが破棄される直前に呼ばれます。この段階で はインスタンスはまだ完全に機能しています。 

  9. https://github.com/vuejs/vue/blob/master/dist/vue.js#L2598 https://jp.vuejs.org/v2/api/index.html#destroyed ◆ 「Vue インスタンスの全てのディレクティブはバウンドして おらず、全てのイベントリスナは削除されており…」 ◆ 状態としては beforeMount と同じ

    ◆ DOM にはアクセスできなくなるので、addEventListener 等してる場合、 beforeDestroy で removeEventListener する必要がある点に注意…? ◆ a タグや location.href で移動したときは このイベントを介さないので注意 destroyed 関数 ※CSRのみ 16 “Vue インスタンスが破棄された後に呼ばれます。このフックが呼ばれるとき、Vue インスタンスの全 てのディレクティブはバウンドしておらず、全てのイベントリスナは削除されており、そして全ての子 の Vue インスタンスは破棄されています。” 

  10. errorCaptured 関数 https://github.com/vuejs/vue/blob/master/dist/vue.js#L578 https://jp.vuejs.org/v2/api/index.html#errorCaptured ◆ 特定のコンポーネントで発生したエラーを hook できる ◆ 上位コンポーネントにも伝搬されるので、止めたい場合は

    return false する。 ◆ 「エラーが捕捉されたときに他のコンテンツを手短に迂回させる、テン プレートにおける条件または描画関数を含めるのが重要」 ◆ エラー前提のハンドリングはしないほうが吉 ◆ errorCaptured は SSR でも使える 17 “任意の子孫コンポーネントからエラーが捕捉されるときに呼び出されます。フックは、エラー、エ ラーをトリガするコンポーネントインスタンス、そしてどこでエラーが捕捉されたかの文字列情報、こ れら 3 つの引数を受け取ります。フックはエラーがさらにもっと伝播するのを防ぐために、 false を 返すことができます。” 
 “このフックでコンポーネントの状態を変更できます。ただし、エラーが捕捉されたときに他のコンテ ンツを手短に迂回させる、テンプレートにおける条件または描画関数を含めるのが重要です。それ 以外の場合、コンポーネントは無限描画ループに投げられます。 

  11. Nuxt.js のライフサイクル ◆ 基本は Vue.js と同じ ◆ Nuxt オリジナルの Lifecycle

    Hook がある (validate , asyncData , fetch ) ◆ グローバルで動くライフサイクル があることに留意 ( plugin, middleware ) 19
  12. Vue.js が拡張された認識でOK 20 ◆ plugin → Nuxt由来 ◆ middleware →

    Nuxt由来 ◆ validate → Nuxt由来 ◆ asyncData, fetch → Nuxt由来 ◆ created → Vue由来 ◆ mounted → Vue由来
  13. https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-app/template/index.js#L243-L255 https://ja.nuxtjs.org/docs/2.x/directory-structure/plugins/ ◆ 弊社だと Firebase の初期化している例が多い ◆ nuxt.config.js の plugins

    に設定する際に mode を指定し てやれば SSR または CSR のみで動くようにできる ◆ Nuxt の App.js では Vue インスタンス生成前に実行 ◆ = beforeCreate よりも前で実行されている plugin ※グローバル 22 “ plugins ディレクトリにはルート Vue.js アプリケーションがインスタ ンス化する前に実行する Javascript プラグインが含まれています。 

  14. https://github.com/nuxt/nuxt.js/blob/9d33456e69a3bc74734461fa2eb866d2ceac490f/packages/vue-a pp/template/App.js#L126 https://ja.nuxtjs.org/docs/2.x/directory-structure/store/#nuxtserverinit-%E3%82%A2%E3%82%AF%E 3%82%B7%E3%83%A7%E3%83%B3 ◆ SSR 時の store に入れるデータの prefetch

    に最適 ◆ Nuxt の App.js では mounted 内で実行している nuxtServerInit 関数 ※グローバル ※SSRのみ 23 “ストアに nuxtServerInit アクションが定義されていて、かつ universal モードの場合、Nuxt.js はコンテキストを渡してこれを呼び 出します(サーバサイドのみ)。これはサーバにある何らかのデータ を直接クライアントサイドに渡すときに便利です。” 

  15. https://github.com/nuxt/nuxt.js/blob/c8a4b91ad40fed316ca91d73cd90c5611828619b/packages/vue-a pp/template/client.js#L248 https://ja.nuxtjs.org/docs/2.x/components-glossary/pages-middleware/ ◆ ドキュメントでも「状態に応じてリダイレクトさせる」 用途に使うと書いてある ◆ global 以外にも、書き方によっては layout,

    pages だけで 動作させることも可能 ◆ Nuxt の App.js では render 内で実行している middleware ※グローバル ※ページ ※レイアウト 24 “ middleware/ ディレクトリ内にファイルを作成することで名前付きミドルウェア を作成できます。ファイル名がミドルウェア名になります。”
 “特定のページにだけミドルウェアを使用する必要がある場合は、関数(もしくは 関数の配列)を直接使用できます”

  16. https://github.com/nuxt/nuxt.js/blob/c8a4b91ad40fed316ca91d73cd90c5611828619b/packages/vue-a pp/template/client.js#L248 https://ja.nuxtjs.org/docs/2.x/components-glossary/pages-validate/ ◆ ルーターのパラメーターの検証で利用する認識でOK ◆ true 以外を return すると

    Nuxt の 404 ページへ遷移する ◆ Nuxt の App.js では render 内で実行している validate 25 “ validate は新しいルートに移動する前に毎回呼び出されます。サーバーサイ ドでは(Nuxt アプリケーションへの最初のリクエストで)1 度、クライアントサイドで は別のルートに遷移する際に呼び出されます。”

  17. https://github.com/nuxt/nuxt.js/blob/c8a4b91ad40fed316ca91d73cd90c5611828619b/packages/vue-a pp/template/client.js#L524 https://ja.nuxtjs.org/docs/2.x/features/data-fetching/#async-data ◆ ページ遷移中にデータを取得したい場合に便利 ◆ 代償として、全てのデータの取得が終わるまで画面描画されないので レスポンスが犠牲になる ◆ Nuxt

    の App.js では render 内で実行している asyncData 26 “asyncData はユニバーサルなデータ取得のための(略)フックです。非同期な 状態を保存するために、コンポーネントのインスタンスにプロパティをセットする (または Vuex アクションをディスパッチする)必要がある fetch とは異なり、 asyncData は単にその返却された値をコンポーネントのデータにマージしま す。”

  18. https://github.com/nuxt/nuxt.js/blob/c8a4b91ad40fed316ca91d73cd90c5611828619b/packages/vue-a pp/template/client.js#L557-L568 https://ja.nuxtjs.org/docs/2.x/features/data-fetching/#fetch-%E3%83%95%E3%83%83%E3%82%AF ◆ Nuxt の App.js では render 内で実行している

    ◆ SSR では asyncData と同時(promise.all ) ◆ created に実行する CSR の fetch とは異なる ◆ ググるとき紛らわしいので注意 fetch(context) ※deprecated 27 “Nuxt 2.12 未満においては、fetch フックは今日の asyncData と非常によく似 た働きをします。この機能は後方互換性のためまだサポートされています: もし fetch() が context オブジェクトを受け取っているなら、それは「レガシー」な fetch フックだと考えられます。この機能は非推奨なので、asyncData(context) や middleware(context) を使用した無名ミドルウェア に置き換えてください。”

  19. https://github.com/nuxt/nuxt.js/blob/c8a4b91ad40fed316ca91d73cd90c5611828619b/packages/vue-app/t emplate/client.js#L896 https://ja.nuxtjs.org/docs/2.x/features/data-fetching/#fetch-%E3%83%95%E3%83%83%E3%82%AF ◆ asyncData と違い、page コンポーネント以外でも利用可能 ◆ beforeMount 前に実行されるので

    SSR にも対応 ◆ ノンブロッキングなので初期描画の速度も犠牲にならない ◆ $fetchState で読み込み状況の取得も可能 ◆ $fetch でリランもできる ◆ Nuxt の App.js では mounted 内で実行している fetch ※ノンブロッキング 28 “ fetch はサーバーサイドレンダリングではコンポーネントのインスタンスが作成され たとき、クライアントサイドでは遷移するときに呼び出されるフックです。fetch フックは 解決される promise を(明示的に、または async/await を使って暗黙的に)返却する べきです:
 
 - 初期ページがレンダリングされる前のサーバー 
 - コンポーネントがマウントされた後のクライアント” 

  20. Server side Rendering 30 ◆ SSR 時は、fetch までを Nuxt が行う

    ◆ beforeMount 以降をクライアント側で処理する →ではない󰢁

  21. ◆ SSR (universal モード) でも CSR はされている ◆ 確かに SSR

    時は created (fetch) までが動く ◇ クライアントサイドでも created が動く ◆ nuxt-link と $router.push での画面遷移は CSR と全く同じ挙動になる Server side Rendering と Client side Rendering 31
  22. ◆ CSR ◆ updated発火 ◆ ページ遷移 (nuxt-link ) イベント発火のタイミング 34

    ←Stacktrace を見ると created 由来 ←Stacktrace を見ると created 由来
  23. Universal mode のライフサイクル (SSR) 35 リクエスト nuxtServerInit Plugin 作成 ※Universal

    と server Middleware 実行 validate asyncData beforeCreate created beforeCreate created fetch Plugin 作成 ※Universal と client beforeMount mounted レスポンス サーバーサイド クライアントサイド 完了 ここの処理はSSR, CSRどちらでも処理される
  24. Universal mode のライフサイクル (CSR) 36 beforeDestroy クライアントサイド 完了 $router.push Middleware

    実行 validate asyncData destroyed beforeCreate created fetch beforeMount mounted SSRと微妙に順番が 違うクセに注意 ※バージョン上がると  変わる可能性もあり 遷移前ページの destroy 処理が始まる前に 遷移後ページの validate, asyncData が動き出す
  25. (参考) SPA mode のライフサイクル 37 リクエスト サーバーサイド レスポンス Plugin 作成

    ※Universalとclientのみ Middleware 実行 validate asyncData beforeCreate created fetch beforeMount mounted 完了 クライアントサイド $router.push ページ遷移後は Universal mode と同じ挙動 予めビルドされた アーティファクトを 返すだけ
  26. SSR が返すレスポンスと受け手 39 この間に何がおきているか リクエスト nuxtServerInit Plugin 作成 ※Universal と

    server Middleware 実行 validate asyncData beforeCreate created beforeCreate created fetch Plugin 作成 ※Universal と client beforeMount mounted レスポンス サーバーサイド クライアントサイド 完了
  27. Hydrate 40 ◆ render と hydrate ◆ React 由来の処理名を Vue

    でも使っているっぽい https://ja.reactjs.org/docs/react-dom.html#hydrate https://ssr.vuejs.org/ja/guide/hydration.html
  28. プラグイン 43 リクエスト nuxtServerInit Plugin 作成 ※Universal と server Middleware

    実行 validate asyncData beforeCreate created beforeCreate created fetch Plugin 作成 ※Universal と client beforeMount mounted レスポンス サーバーサイド クライアントサイド 完了 Universal な plugin に環境依存なコード(document とか window とか fetch とか) が含まれていると、サーバーサイドで実行できずにエラーになる ※だから axios みたいなユニバーサルで動くプラグインが重宝される
  29. 認証 44 リクエスト nuxtServerInit Plugin 作成 ※Universal と server Middleware

    実行 validate asyncData beforeCreate created beforeCreate created fetch Plugin 作成 ※Universal と client beforeMount mounted レスポンス サーバーサイド クライアントサイド 完了 認証処理を middleware でやるなら サーバーサイドで本人確認する仕組みが必要 (Cookie とか Session とか) クライアントサイド $router.push Middleware 実行 validat asyncDa サーバー側で本人確認してないから process.client して判定する みたいなフローにしちゃうとSSR ではページが表示される (CSR のときだけ認証チェックが走る)ことになる → バグ
  30. SSR は難しい 46 ◆ SPA や SSG で要件を満たすかよく確認する ◆ SSR

    しなくていいなら使わないのがベター ◆ ただし mode や target の切り替えは高コスト ◇ 環境依存コードは絶対混入する(にんげんだもの) ◆ MVP と割り切って SPA で走り抜ける選択肢も持っておく ◇ PO / SO に提案できるように理解しておく ◆ SSR する場合はちゃんと振る舞いを理解して使う ◆ 理解がないと困ったバグが混入しがち
  31. Vue 3 っぽくComponent を書くことが Nuxt 2 でもできる 50 ◆ @nuxt/composition-api

    ◆ https://composition-api.nuxtjs.org/ ◆ Vue 3 でも Options API がデフォ ◆ Composition API は「玄人向け」という位置付け ◇ https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api ◆ Class API で Composition が使えるようになる bridge APIも提供予定 ◆ https://github.com/vuejs/vue-class-component/issues/402