少し詳しく読み解いてみる Nuxt TypeScript での asyncData とその周辺
こんにちは。UI/UXデザインを担当しているsadakitchenです。
最近はWebのフロントエンドも携わっています。
バーチャルキャストのWebフロントでは Nuxt.js や vue を活用しており、パフォーマンスの高いアプリケーションを構築しています。
更に、 TypeScript によって型安全性による恩恵を得るために Nuxt TypeScript を使っています。
(参考記事:nuxt.js による UX/DX フレンドリーな Web サービス開発)
今回は Nuxt TypeScript でよく使っているメソッド asyncData とその周辺について少し詳しく読み解いてみます。
なお、Nuxt&TypeScriptの環境構築については本記事では取り扱いません。
以下のような記事を参照してみてください。
3分でつくる2019年版 Nuxt.js TypeScript 開発環境設定(Qiita)
この記事の対象者
- Nuxt.js を触ったことがあるが、深堀りしたことはない人
- TypeScript は使っているが、Nuxt.js はあまり触ったことがない人
環境
- Nuxt v2.12.2
- @nuxt/typescript-build v0.6.6
- @nuxt/typescript-runtime v0.4.8
- nuxt-property-decorator v2.7.2
- @nuxt/http v0.5.1
サンプルコード
SSR (universal) モードの Nuxt のページコンポーネントで、asyncData
メソッドから API を叩いて、その結果を返すようなものを作ります。
次のコードは HogePage
ページコンポーネントをクエリ id
を含んで表示すると、 json データを返す API を叩き、stringが変える想定の作りになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// (前略) <script lang="ts"> import { Component, Vue } from 'nuxt-property-decorator' import { Route } from 'vue-router' import { NuxtHTTPInstance } from '@nuxt/http' @Component export default class HogePage extends Vue { async asyncData({ query, $http, }: { query: Route['query'] $http: NuxtHTTPInstance }): Promise<{ title: string }> { const res: { title: string } = await $http.$get( `http://localhost:3000/api/news?post=${query.id}` ) return { title: res.title } } } </script> // (後略) |
深堀りしてみる
では各コード深堀りしていきます。
1 |
<script lang="ts"> |
TypeScriptを使う際に lang="ts"
と記述しています。
変数を扱う際に string や number といった「型」を明示的に使えるようになり、エラーチェックがより厳格になります。
1 |
import { Component, Vue } from 'nuxt-property-decorator' |
nuxt-property-decorator
とは、 vue-property-decorator に加え、 Nuxt の独自のメソッドやプロパティなどもデコレートしてくれるモジュールです。
このモジュールを使用することで、クラスベースでの記述が可能になり、Component
やProp
などをクラスの構成要素(クラスメンバ)として、@Component
や@Prop
という形で見通しよく記述することができます。(クラスに対して直接プロパティを定義できる)
nuxt-property-decorator の依存関係は以下のようになります。
デコレーターとは?
クラス宣言やメソッド、アクセサ、プロパティ、パラメーターに結び付けられる特別な宣言の一種
と説明されますが、もう少しわかりやすくすると、
「あるコードを他のコードで内包しつつ実行するもの」です。
例えば、
あるコードが Hello,world.
を返す場合、
他のコードで内包していると、Hello,world.
を含んで返す実装ということになります。
JavaScriptでは「@」をつけて記述することで表現できます。
なお、 Nuxt TypeScript でデコレーターを扱う場合は tsconfig.json の compilerOptions に
"experimentalDecorators": true,
と記述する必要があります。
デコレーター、非常に説明が膨大なため、この記事ではサンプルコードの実装に関わる部分まで解説します。
1 2 |
import { Route } from 'vue-router' import { NuxtHTTPInstance } from '@nuxt/http' |
asyncData の 引数 query と $http の型を定義するために読み込んでいます。
1 2 |
@Component export default class HogePage extends Vue { |
Component を クラス宣言 するために、デコレーターを利用する記述です。
この記述により HogePage クラスを Vueコンポーネントとして扱うことになります。
1 2 3 4 5 6 7 |
async asyncData({ query, $http, }: { query: Route['query'] $http: NuxtHTTPInstance }): Promise<{ title: string }> { |
今回APIを叩くことで、非同期データを扱うことになるため、 async
を追加しています。
asyncData内部には await
が入ります。
asyncDataの第一引数には context オブジェクトが入るのですが、その中から使用するキーだけを記述しています。
今回はクエリの id
を取得したいため、 route.query
のエイリアスとなる query
、非同期処理を行うためのHTTPモジュール $http
を記述しています。
返り値は非同期処理となるため、Promiseで返しています。
asyncDataとは?
asyncData はサーバーからのデータを非同期で取得し、 Nuxt.js のコンポーネント内で扱う準備をするメソッドです。
以下の特徴があります。
- ページコンポーネントでのみ実行される
- ページ表示時に毎回実行される(Nuxt.jsのライフサイクルイベント)
- 第一引数に context オブジェクトが入る
- asyncData内で this は使えない
- asyncData実行時にはコンポーネントのインスタンスが生成されていないため
- Nuxt2.12でfetchが全てのコンポーネントで使え、thisも使えるようになったのでそっちを使うのもありかも
- asyncData実行時にはコンポーネントのインスタンスが生成されていないため
- asyncDataの返り値は data と統合される
- 非同期の場合、asyncDataの返り値は Promise
となる
またasyncDataがNuxtフレームワーク内で実行される順番(ライフサイクル)は以下のようになっています。
詳しくはVue.js と Nuxt.js のライフサイクル早引きメモ(Qiita)を参照ください。
contextとは
特定のライフサイクルで使用できるオブジェクトです。
様々なキーが入っています。
クライアントサイド、サーバーサイド共通
- app
- store
- route
- params
- query
- env
- isDev
- isHMR
- redirect
- error
クライアントサイドのみ
- from
- nuxtState
サーバーサイドのみ
- req
- res
- beforeNuxtRender
なお、Nuxt.jsのプラグインによっては、 $http
や $i18n
というように $
をつけて参照することができます。
1 2 3 4 |
const res: { title: string } = await $http.$get( `http://localhost:3000/api/news?post=${query.id}` ) return { title: res.title } |
HTTPモジュールを利用して、APIを叩き、返ってきた title キーを含む json 形式の中から title データを返しています。
API を叩く際のモジュールは http ではなく、 axios でも構いません。
なお HTTPモジュール には axios と比較してこのような違いがあります。
- 軽量
- fetch API を利用するので service worker との親和性が向上
- node.js 側は node-fetch を用いてほぼロジックなしで対応
(参考記事 nuxt.js は axios を捨てて ky になる様子(Qiita) )
IEを対象としないサイトの場合は、HTTPモジュールを使うのがよいのではないでしょうか。
proxyについて?
なお余談ですが、外部ドメインのAPIを叩いた際に CORS に関するエラーが発生する場合があります。
その際は @nuxtjs/proxy
でプロクシを介してアクセスすることでエラーを回避できます。
以下サンプルコードを記載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// (前略) /* ** Nuxt.js modules */ modules: ['@nuxtjs/style-resources', '@nuxt/http', '@nuxtjs/proxy'], // モジュールに登録 http: { proxy: true, // proxy を使う }, proxy: { '/api/': { target: 'http://api.example.com', // 実際のAPIのURL pathRewrite: { '^/api/': '' }, // HTTPモジュールで叩いたURLの `/api` を削除 }, }, // (後略) |
注意点として、nuxt generateで静的なウェブアプリケーションとしてビルドした際には使用できません。
本番ではきちんとサーバー側で CORS の設定を行いましょう。
まとめ
以上のように asyncData とその周辺を読み解いて見ました。
重要なポイントとしては以下となると思います。
- デコレータ
nuxt-property-decorator
を使うことでVueコンポーネントを拡張できる - asyncDataの引数 context オブジェクトには Vue に関する便利なキーが入っている
- asyncDataの返り値は Vue コンポーネントの data と統合される
- CORSの場合は
@nuxtjs/proxy
を使う
Nuxt.js は非常に強力なフレームワークで使いこなすのがとても大変です。
が、公式ドキュメントを読みつつ、コードを直接見ていくと、気づかなかった振る舞いを見つけることもできるとおもいます。
一つ一つ内容を読み込み、振る舞いを理解することで、使いこなせる第一歩になるのではないでしょうか。
- Category
- sadakitchen /
- 技術
- Tag