心理的負担を抑えつつVue.jsを0.12→2.4にアップグレードした話

こんにちは、@mugi_uno です。

RubyKaigi盛り上がりましたね〜!

そして広島は美味しいものがたくさんでした。次回の仙台も楽しみですね!


さて、みなさんはフロントエンドのフレームワークには何を利用していますか?

Misocaでは一部機能でReact/ReduxによるSPA構成を採用しています。 めろたん(@renyamizuno_)の書いた過去のエントリーにも登場していますね。

tech.misoca.jp

しかし、Misocaで利用しているのはReactだけではありません。

jQuery

React/ReduxはSPAなどでパワーを発揮しますが、逆に気軽に取り回しにくいため、ちょっとしたコンポーネントを作りたいケースなどでは、さくっと書けるjQueryを利用することもまだまだ多いです。

しかし、DOM操作が柔軟すぎるため影響範囲が読めなかったり、ビューと状態の管理が複雑になったりという問題を抱えやすいです。

そこで…

Vue.js

f:id:mugi1:20170926111531p:plain

実は、すでに部分的にVue.jsが採用されていました。 Vue.jsには以下のような特徴があります。

  • Rails側のViewをベースにディレクティブ/コンポーネントを埋め込める。
  • 単一ファイルコンポーネント内にtemplate/script/styleを閉じ込めることができる。
  • 日本語のドキュメントが充実している。
  • Reactと比較すると、フロントエンドが苦手な人でもわりと理解しやすい(と思う)

また、個人的な印象ですが、同一画面に小さいコンポーネントが複数点在する場合などにおいては、Reactよりも書きやすいように感じます。

ということで、すでに使えることですし、どんどん使っていきましょう!!

…と言いたいところですが

f:id:mugi1:20170926114710p:plain

衝撃のバージョン0.12.16。リリースされたのはなんと、2015年9月です。

単一ファイルコンポーネントをはじめとする、様々な魅力的な機能が使えないことがわかりました。

古いものを利用し続けるとエンジニアのモチベーションが下がりますし、情報を追うのも時間とともに困難になっていきます。

これは良くない!

アップグレードだ!!

というわけで、今回はMisocaで利用しているVue.jsを 0.12→2.4.2にアップグレードした際のお話です。

アップグレードの流れ

心理的負担を抑えるための下準備をする

0.12から2.4.2という大ジャンプのため、 尋常ではない数のBreaking Changesが存在します。

マイグレーションガイドも古すぎて存在せず、地道に差分を見ていかねばなりません。

リスクを抑えるためには少しずつ置き換えをしていきたいところですが、 すでにVue.jsはnpmで管理しているため、単純にアップグレードすると、すべてのVue.js利用箇所が一気に2.4に変更されます。

影響範囲が広すぎて心理的な負担も大きく、レビューする側も大変です。PRのマージボタンを命懸けでクリックすることになりますね。

そこでまずは、小さい範囲で少しずつアップグレード可能となるような環境を整えました。

1. ローカルパッケージをつくる

まず、サブディレクトリを作成し、0.12のVue.jsのみをdependenciesに保持するpackage.jsonと、エントリーファイルを作成します。

node_legacy_dependencies/vue-legacy/package.json
node_legacy_dependencies/vue-legacy/index.js

package.jsonの内容(dependencies以外は重要ではないのでデフォルトのままです)

{
  "name": "vue-legacy",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "0.12.16"
  }
}

index.js

module.exports = require('vue');

※以下を参考にさせていただきました。
http://efcl.info/2016/05/02/npm-package-mixed-multiple-versions-demo/

2. プロダクトで利用するpackage.jsonの依存を更新

Vueのバージョンを2.4.2へ更新し、さらに上記で作成したローカルパッケージを、vue-legacyでimportできるように依存に追加します。

(package.jsonから抜粋)

"vue": "^2.4.2",
"vue-legacy": "file:node_legacy_dependencies/vue-legacy",

3. 既存のすべてのVue利用箇所のrequire/importの向き先をvue-legacyに変更する。

以下のように変更します。

import Vue from 'vue-legacy';

→結果どうなるか?

import時の指定により、バージョンを任意に切り替えることができるようになりました。

import LegacyVue from 'vue-legacy'; // 0.12.16
import Vue from 'vue'; // 2.4.2

これで、アップグレードを行いたい利用箇所のみ、vue-legacyvueと書き換えることで、影響を最小限に抑えながら変更していくことができます。

置き換え対象がどれだけ残っているかも、vue-legacygrepするだけで確認できるので便利ですね。

アップグレード作業

これで小さくアップグレードを進めるための準備が整いました。実際に更新をしていきましょう!

CoffeeScript→ES6への変換

変更前のVue.jsを利用しているコードは、ほぼ全てがCoffeeScriptで記述されていました。

Misocaでは新規にjsコードを作成する際は基本的にはES6を利用しており、アップグレードに伴い大幅な変更も予想されますので、このタイミングでCoffeeScriptからES6への変換も行うことにしました。

手で書き換えるのはツラいものがあるので、decaffeinateを利用しましたが、特に問題もなく変換することができ、とても助けられました。

github.com

プロダクトによってはコーディング規約に揃えるのが大変だったりするかもしれませんが、ひとまずES6にしたい!というケースでは、一度decaffeinateで変換してから細かい修正をしていくと、安全に進められるかもしれません。

変更点を確認しながらがんばって2.4に置き換える

あとは地道にVue2.4で動くように書き換えていきます!

  • 短縮記法の利用 (v-bind:hoge='abc':hoge='abc')
  • 単一ファイルコンポーネントへの書き換え
  • フック関数変更に伴うカスタムディレクティブの書き換え
  • vm.$setをはじめとする、非推奨となったAPIを全て書き換え
  • クラス/スタイルのバインディングの書き換え
  • などなど…

多すぎて載せきれませんが、最終的にはかなりの量の書き換えを行いました。

作業中は、Vue1→Vue2のマイグレーションガイドを常に見ていたような気がします。

また、移行ヘルパーも適宜実行して機械的なチェックも行いました。

ファイル単位でPR&レビューを繰り返す

下準備のおかげで、影響範囲を抑えつつアップグレードしていくことが可能になっているので、上記変更をファイル単位で細かく繰り返していきます。

(大量のPR)

f:id:mugi1:20170926140546p:plain

作業中に軽微なバグを見つけることもありましたが、影響範囲が閉じられているので、Bugfixも小さいPRとして適用し、柔軟に対応していきました。

そして…

全ファイルから vue-legacy が消え去ったのを確認し、無事に移行完了となりました。

最後の作業として、下準備で作成したローカルパッケージを削除して終了です。

f:id:mugi1:20170925113819p:plain

f:id:mugi1:20170925114934p:plain

これで心置き無くVue.jsを使っていけるようになりました。

まとめ

こういった作業をする際は往々にして影響範囲が広く、リスクも高いことが多いです。今回は、小さく進める方法を最初に検討して整えることで、不安な気持ちを抑えながら作業していくことができました。

レビューする側の負荷軽減にも繋がるので、今後も心がけていきたいですね!


採用

Misocaでは最新フレームワークを使っていきたいエンジニアを募集しています!