Nuxt.jsモジュールを自作して開発効率を向上させる

Nuxt.js のモジュールを自作して簡単に使い回す

基礎知識があるのとないのとでは理解度に違いがあるので、 モジュールとは何?という方はこちらを読んでみてください。 Nuxt.js はドキュメントが多くて助かりますね!

引用

この記事は @mya_ake さんの Nuxt.js と axios のエラーハンドリング周りの話 から引用させていただきました。 この場を借りてお礼申し上げます。

対象読者

  • 「エラーハンドリングで冗長なコードを書きたくない」と考えている方
  • 「他のプロジェクトでもコードを使い回ししたい」と考えている方
  • 個人開発をしている方

はじめに

Nuxt.js にはモジュールという Nuxt のコア機能を拡張するもの存在します。 有名所は以下の Github にリストがあります。 nuxt-community / modules 実際に使っている方は多いのではないでしょうか。 しかし、自作するとなると敷居が高いように感じるかもしれません。

公式にはこう書いてあります。

モジュールは、 Nuxt 起動時に順番に呼び出される、シンプルな関数です。

モジュールは、結構簡単に作れるのです。 作成したモジュールは npm パッケージとして公開することもできます。 この記事では npm パッケージを公開することはしません。

車輪(タイヤ)の再発明をしないようにするためにも、今回は例を用いて Nuxt モジュールを自作していきたいと思います。

モジュール作成前に

引用ですがモジュールを作成するには理解しておく必要のある知識です。

moduleOptions これは modules の配列を利用するために、モジュールの利用者から渡されるオブジェクトです。これを使うことで modules のふるまいをカスタマイズすることができます。

this.options この参照を利用して Nuxt options へ直接アクセスすることができます。これはすべてのデフォルトのオプションがアサインされた、ユーザーの nuxt.config.js の内容です。モジュール間で共有されるオプションとして利用できます。

this.nuxt 現在の Nuxt インスタンスへの参照です。利用可能なメソッドは Nuxt クラスのドキュメント を参照してください。

this モジュールのコンテキストです。利用可能なメソッドは モジュールコンテナ クラスのドキュメントを参照してください。

module.exports.meta この行は npm パッケージとして公開するときは必須です。Nuxt はあなたのパッケージをより良く機能させるために、内部でメタ情報を利用します。

プロジェクトの用意

今回プロジェクト名はnuxt-modulesとします。

$ npx create-nuxt-app nuxt-modules
or
$ yarn create nuxt-app nuxt-modules

モジュール作成

今回は axios モジュールを拡張したモジュールを作成していきます。 axios で通信を行う際にエラーハンドリングを各所に書くのは面倒なので共通化したいと思います。 プロジェクト毎にプラグインファイルを配置してもいいですが、npm モジュール化することでプロジェクトへの導入が楽になります。

では早速モジュールを作成していきましょう。 先ほど作成したプロジェクトへ移動します。

$ cd nuxt-modules/

ディレクトリの用意

今回作成するモジュールはmodules/resourcesディレクトリに作成してくので、 modules/resourcesディレクトリを作成します。

$ mkdir modules
$ mkdir modules/resources
$ cd modules/resources

ファイルの用意

modules/resourcesディレクトリへ必要なファイルを作成します。

$ touch module.js plugin.js Resources.js Response.js

ここまで出来たら以下構造になっていると思います。

/
    assets/
    components/
    ...省略
    modules/
        - resources/
            - module.js
            - plugin.js
            - Resources.js
            - Response.js
    ...省略
    nuxt.config.js
    package.json

とりあえず動くサンプルの作成から

それぞれのファイルは以下のようになります。 動くサンプルとしてクラスを定義し、インスタンスのタイミングでログを出力しているだけです。

// module.js
import path from 'path'

// モジュールルート
const libRoot = path.resolve(__dirname, '..')

function resourceModule (_moduleOptions) {

    // nuxt.config.jsで定義されているオプションを取得
    const moduleOptions = { ...this.options.resources, ..._moduleOptions }

    // プラグインに登録
    // function名.call(this) でthisを共有できる
    copyResources.call(this)
    copyResponse.call(this)
    copyPlugin.call(this, { options: moduleOptions })
}

// Resource.jsをコピー
function copyResources() {
    this.addTemplate({
        src: path.resolve(libRoot, 'resources', 'Resources.js'),
        fileName: path.join('resources', 'Resources.js')
    })
}

// Response.jsをコピー
function copyResponse() {
    this.addTemplate({
        src: path.resolve(libRoot, 'resources', 'Response.js'),
        fileName: path.join('resources', 'Response.js')
    })
}

// plugin.jsをコピー
function copyPlugin({ options }) {
    this.addPlugin({
        src: path.resolve(libRoot,'resources', 'plugin.js'),
        fileName: path.join('resources', 'plugin.js'),
        options
    })
}

export default resourceModule
// plugin.js
import Resources from './Resources'
import Response from './Response'

export default (ctx, inject) => {
  new Response()
  new Resources()
}
// Resources.js
export default class Resources {
  constructor() {
    console.log('Resources')
  }
}
// Response.js
export default class Response {
  constructor() {
    console.log('Response')
  }
}

確認

どちらを使っているかで実行方法が違うので自身の環境に合わせてください。

$ npm run dev
or
$ yarn dev

結果

Response
Resources

実際に処理を書いていく

ここまできたらあとは実際の処理を書いていくのみです。

Resources.js axios をラップしたクラス。 エラーハンドリングの共通化を行います。

Response.js レスポンスを加工するクラス。 レスポンスの共通化を行います。

以上を踏まえて実装していきます。

// Resources.js
export default class Resources {
  constructor(axios, responseBuilder) {
    this._axios = axios
    this._responseBuilder = responseBuilder
  }

  // get関数を提供
  async get(url, config) {
    const response = await this._axios
      .get(url, config)
      .catch((err) => err.response)
    return this._buildResponse(response)
  }

  // レスポンスを加工する関数が存在すれば呼び出すコールバック関数
  _buildResponse(response) {
    if (typeof this._responseBuilder !== 'function') {
      return response
    }
    return this._responseBuilder(response)
  }
}
// Response.js
export default class Response {
  constructor(response) {
    this._rawResponse = response
    this._expectStatus = 200
    this._buildResponse()
    return this
  }

  // 正常な場合のステータスコードを設定する
  expectStatus(expectStatus) {
    this._expectStatus = expectStatus
    return this
  }

  // レスポンスのdataプロパティをclassのインスタンスへと変換する
  toModel(Model) {
    if (this.isError) {
      return this
    }
    this.data = new Model(this.data)
    return this
  }

  // Vue.jsのcomputed的なもの
  // ただし結果はキャッシュされないので参照があるたびに関数が実行される
  get isError() {
    return this.status !== this._expectStatus
  }

  // 必要なプロパティだけ抜き出している
  _buildResponse() {
    const { status, data } = this._rawResponse
    this.status = status
    this.data = data
  }
}

plugin.js も修正します。

// plugin.js
import axios from 'axios'
import Resources from './Resources'
import Response from './Response'

// レスポンスを加工する関数
// 今回はResponseクラスのインスタンスを返すようにしている
const responseBuilder = (response) => {
  return new Response(response)
}

export default (ctx, inject) => {
  const resources = new Resources(axios, responseBuilder)
  ctx.$resources = resources
  inject('resources', resources) // これでいろいろなところで使えるようにする
}

動作確認

nuxt-modules/pages/index.vue に処理を追加します。 確認の為、qiita からデータを取得します。

export default {
    ...省略
    async asyncData(ctx) {
        const url = 'https://qiita.com/api/v2/items?page=1&per_page=5&query=DDD'
        const response = await ctx.$resources.get(url)
        response.toModel(Items)
        if (response.isError) {
            ctx.error({
                statusCode: response.status,
                message: response.data.message
            })
            return
        }
        const { items } = response.data
        return { items }
    }
}

まとめ

今回は、Nuxt モジュールに焦点を当てて記事を書きました。 Nuxt は記事が多い印象でしたが、Nuxt モジュールに関する記事は少なかったです。 (公式の@nuxtjs/axios や@nuxtjs/auth のコードを追いながら作成しました。)

実際のプロジェクトに導入する場合はプロジェクト毎にエラーハンドリングやコーディングルール等が違ったりするケースが多いことも多く、その際は共通化に躍起にならない方がいい場合もあります。

(個人開発を Nuxt でしている方で「エラーハンドリングで冗長なコードを書きたくない」や「他のプロジェクトでも使い回したい」と考えている方にオススメの方法だと個人的に思っています。仕事ではこの辺りを個人の裁量で決めれないことも多いため。)

今回作成したモジュールは axios のエラーハンドリングを共通化したものでした。 今回のモジュールを拡張(POST、PUT、DELETE 関数を提供するように)したり、アクセスログをファイルに書き込んだりとあなたの工夫次第で色々できるので試してみてください。

参考

Nuxt.js 公式 Qiita API V2 Documentation @mya_ake さんのNuxt.js と axios のエラーハンドリング周りの話のコード

PR

カナエテ では、Nuxt.js の技術支援も行っています。フロント開発や設計など一部分からお請けすることも可能ですのでお気軽にお問い合わせください。

お問い合わせフォーム

コメント

この記事へのコメントはありません。

関連記事