Vue on Laravelというモノリスを解体してNuxtへ移行しました!

こんにちは、株式会社SCOUTERの開発部の小平(@ryotakodaira )です。

f:id:ryotakodaira:20181212011618p:plain 以前Twitterにてこのようなツイートをして、Nuxt移行の記事を書くと宣言してしまっていたので、書く書く詐欺で終わらないように今回の開発の背景などを含めて記事にしてみました。

弊社では、SARDINEという法人向けの業務管理ツールを提供していますが、そのアプリケーションはLaravelがベースにあり、その中でVueを使う構成で運用されていました。

今回、そのようなLaravelに依存しているVueアプリケーションからフロントエンドのみを切り出す形でLaravel API+Nuxtアプリケーションに置き換えを行いました。

この記事では、なぜNuxtに移行したのかや、その時に少し躓いた点について記していこうと思います。

なぜフロントを切り出す選択をしたのか

今回リニューアルをしたアプリケーションはリリースから1年6ヶ月程が経過しており、当然のことながら同期間立て続けにシステムの追加開発/運用を続けてきました。

当初は立ち上げから間もないサービスということもあり、スピード重視での開発体制であったため、アプリケーション構成も最小構成のLaravel(bladeテンプレート)の中でVueを使う構成で開発を進めていました。

機能追加を繰り返しコードベースの量が増えたり開発に関わるメンバーが増えたことによって以下のような問題が顕在化してきました。

ページ読み込みにかかる時間が長いことによるユーザビリティの低下

  • 前述の通り、Laravel(bladeテンプレート)の中でVueを使う構成で作られていた
  • そのためクライアント側のルーティングをLaravel(PHP)が握っている構成でアプリケーション自体が構築されておりvue-routerなども使用していなかったため、ページ遷移のたびに「JS/CSSファイルのダウンロード→Vueインスタンスの初期化」が行われてしまっていた

採用要件を高めてしまっている

  • バックエンド、フロントのコードが完全に分離出来ていないため、LaravelとVueの両方に理解がないと1つの機能開発を行うことが出来ない状態になっていた
  • 仮に両方出来たとしてもバックエンド、フロントのそれぞれの責務範囲がはっきりしていなかったためキャッチアップにも時間がかかってしまう

開発効率の低下

  • はるか昔に導入した化石のようなパッケージ群の影響範囲が分からず毎回影響範囲を調査する必要があった
  • ESLintやPretterを入れていなかったためコードスタイルが無法地帯
  • モノリシックな構成になっているためバックエンド、フロントの両方を気にかけて開発を行わないとならない
  • コードが増えるにつれてjavascriptのビルドの時間が長くなり開発中の待ち時間が増加(laravel-mix)

ということでそれらを一気に解決するために、フロントを切り出す開発に踏み切りました。

移行時に気をつけたこと

VuexStoreの設計

  • NuxtのSPAモードで開発するので、今までとは違ってページ遷移するたびにStoreが初期化されない(元々はページ遷移でどうせ初期化されるからちゃんとやってなかったw)
    • ページ遷移してもStoreは引き継がれるため遷移先の挙動に影響を及ぼさないように調整
  • コンポーネント内にdataプロパティーとして状態を持っている場合、ページ遷移でコンポーネントが破棄されると当然dataプロパティーも破棄されるので同じページに戻ってきた時に再度リクエストが走り、無駄が生じてしまう可能性がある
    • Vuexをフル活用しグローバルに状態を持つように変更して該当ページに再流入してきた時はAPIにリクエストを送らずにVuexからデータを降ろしてくるように処理を修正する

移行時に躓いたこと

驚くべきことにNuxtへの移行に大きな問題には遭遇しませんでしたが、 唯一少し躓いたところがありましたので、その部分の説明をします。

this.$router.pushの仕様

this.$router.push('hoge', {
  query: {
    params: ['foo', 'bar']
  }
})

Nuxtで上記のようにルーティングのプッシュを行うと、デフォルトでは http://example.com?params=foo&params=bar のようなURLになります。

ただ、このクエリーパラメータのフォーマット形式だと弊社のサービスにとっては問題が有りました。

Nuxtのページコンポーネントで受け取った query のオブジェクトが意図した形では無いという問題でした。

// http://example.com?params=foo&params=bar

query = {
  params: ['foo', 'bar']
}

// http://example.com?params=foo

query = {
  params: 'foo'
}

👆配列で欲しいが文字列になってしまう

要件としてはparamsが一つだったとしてもパースした結果は配列として受け取りたいので、nuxt.config.jsに以下のような記述を追加してvue-routerを拡張して解決しました。

query-stringというnpmパッケージを通すことによってクエリパラメーターのフォーマットを指定できるのでその方法を採用しました。

ちなみにquery-stringを通すとURLは http://example.com?params[]=foo&params[]=bar のようになります。

module.exports = {
  router: {
    parseQuery(query) {
      return require('query-string').parse(query, {
        arrayFormat: 'bracket',
      })
    },
    stringifyQuery(params) {
      if (Object.keys(params).length === 0) {
        return ''
      }
      const query = require('query-string').stringify(params, {
        arrayFormat: 'bracket',
      })
      return `?${query}`
    },
  },
}

まとめ

VueをNuxtへの移行は今回が初めてでしたが、移行中は大きな問題は特に発生せずNuxtはまさにかゆいところに手が届くという言葉がふさわしいなと実感しました。

弊社と同じような課題を抱えている開発チームにはぜひ取り組んでみて欲しいと思います!

今回のNuxtへの移行にはエンジニアメンバー4人で3週間かかりましたが、顕在化していた問題は無事解決されたため、リターンとしては十分で合ったと思います。

ただ、Nuxtへ移行する前のLaravel(bladeテンプレート)の中でVueを使う構成のアプリケーションが他にも存在しているためタイミングを見てそちらもNuxtに移行していきます。

次は2回目なのでもっと早く終わると信じて。

さいごに

SCOUTER社の開発チームの取り組みを紹介させていただきました! サービスが成長していくにあたって、これからもメンバーを増やしていきたいと思っています。

興味のある方は下記から応募いただくか、@ryotakodairaにご連絡ください!!

www.wantedly.com

www.wantedly.com

www.wantedly.com