ページ

2016-11-02

Vue.js 1.x から 2.0 への移行のポイント

先日Vue.js 1.xから2.0への移行を行った。まるっと書き換えないといけないというレベルではないが、メジャーバージョンアップだなぁと感じる程には違いを感じられたのでいくつかポイントを書いておく。

もちろん、Vue.jsの機能を全て使いこなしているわけではないため、すべて網羅しているとはとても考えていないので、詳しくは「Migration from Vue 1.x」を参照してほしい。

大きな違い:テンプレートが事前コンパイルできるようになった

Vue 1.xと2.xの大きな違いは、テンプレートのコンパイルと実行を分離できるようになったこと。実際に移行してみた感じ、このコンパイル処理の邪魔になるようなことに関しては排除するような形で変更されているように見える。

このコンパイルというのがどのようなものかは、browserify + vueifyの処理結果を見るとわかりやすい。

Single File Components (.vue)という形でコンポーネントを記述した場合、スクリプトやスタイル、テンプレートを同一のファイルに記述できる。browserifyを使う場合、vueifyというプラグインがこの.vueをJavaScript から読める形に変換してくれるのだが、この処理結果が1.xから2.xで大きく変わっている。

コンパイル対象のテンプレートが以下だとする。

Vue 1.xの場合は以下の形。

template変数に文字列として代入するだけになっている。

Vue 2.xの場合は以下の形。

render関数に、DOM要素を作っていくような関数がセットされていることがわかる。JSX中の要素がReact.createElement関数に変換される構造に近い。

移行の方法

Migration from Vue 1.x」の頭のほうに書いてあるが、vue-migration-helperを実行し表示されたメッセージの対処後、実行時に表示される警告を見つつ対処していく形になる。

移行時に変更する必要のあった点

browserify + vueifyで利用しているため、それを前提とする。webpackでも似たようなことをしないといけないはず。

npmインストールのデフォルトがRuntime-only Buildとなることへの対応

npmを利用すると、「Runtime-only build」を利用するようにインストールされる(Standalone vs. Runtime-only Build)。このRuntime-only buildは、template: によるテンプレートコンパイルが行えないため、HTML中にテンプレートを書いて実行する方式で実装されているアプリケーションは実行できなくなる。対処する方法は2通り。

render関数の実装(Runtime-only buildで実行できるようにする)

「Failed to mount component: template or render function not defined」と言われるので、その通りrender関数をnew Vue()の指定につける。この場合、ルートとなるコンポーネントも.vueに記述し、HTML側にはマウントされる要素しか用意しない。

HTMLは、

などとし、.js側は

というようにする。

SPA的なアプリケーションであれば十分であるかもしれないが、ちょいちょいVueを利用しているようなアプリケーションの場合、マウントの呼び出しがちょいちょいあるということなので、propsを受け取れるようなマウント関数を作っておくと便利である。


詳しくは「Render Functions」を参照。

Standalone buildの利用

既存のHTML中のテンプレートもコンパイルする場合、Standalone buildを利用するように変更する。

browserify + vueifyの場合は、aliasifyによってimport Vue from "vue"の動きを書き換える

まずaliasifyをインストール。
$ npm install aliasify --save-dev

package.jsonに以下の記述を追加。

browserifyの呼び出しに「.transform(aliasify)」を追加する。

属性中の{{ }}をv-bindと式に変更する

属性中の{{ hoge }}は解釈されなくなったため、v-bindと式に変更する必要がある。

今まではこのような形だったものは


このように変更する必要がある。


v-model中のフィルタの廃止

今まではv-model="obj.val | filter"という形で、v-modelにもフィルタが適用できていたのだが、これが廃止になった。

「v-model="something"」が「<input v-bind:value="something" v-on:input="something = $event.target.value">」のsyntax sugarである(Form Input Components using Custom Events)ということを利用して、コンポーネントを作る必要がある。

実際の例は、Migration from Vue 1.xに書いてあるが、v-modelに対するフィルタに比べると手間なので、多用していた場合は、結構痛いと思う。

Vue.config.delimitersをコンポーネントレベルに変更する

Vue.config.delimitersは廃止になり、"{{"と"}}"の変更はコンポーネントレベルでしか受け付けなくなった。

サーバサイドテンプレートとの混乱を避けるため、デリミタは変更していたのだが、このような場合new Vue()の呼び出しにそれぞれつける必要がある。

ただし、先述の通りvueifyがテンプレートをコンパイルするようになったのだが、vueifyが.vueに設定されたdelimitersを解釈しないため、変更ができなくなった。そのため、.vueで変更されたデリミタを使用している場合、デリミタ自体を全て書き換える必要がある。

propsでのJavaScript予約語の利用不可

コンパイル結果を見たらそれはそうかという感じだが、コンポーネントプロパティに予約語を使うと怒られるようになった。地味に痛いのが"class"で、注意が必要。

readyライフサイクルイベントをmounted+αに

"ready"ライフサイクルイベントが廃止になった。

一番近いのがmountedで、必要であればvm.$nextTickでDOM要素が構築されてから処理を実行する必要がある。

v-for中の$indexの代わりに(value, index)で受ける

$indexは削除されたので、(value, index) in valuesの形で受ける必要がある。

v-elとv-refはref="hoge"とvm.$refs.hogeへ

テンプレート中のDOM要素及びコンポーネントをスクリプトから利用できるv-refはref属性に変更になった。ref="hogehoge"とつけると、this.$refs.hogehogeと参照できる。

transitionの構造変更

transitionの構造が変更になった。

今までは"transition"属性をつけると適宜v-transition, v-enter, v-leaveクラスが付与されるという形だったが、transition要素で囲むと〜-enter, 〜-enter-active, 〜-leave, 〜-leave-activeが付与されるようになった。

タイミングも違うので、「Transition Classes」の図を見て対処する必要がある。

おわりに

自分のところではまったのはこんな感じ。ガイドはもっと長いので、もっと大きなアプリケーションでは他にもあると思う。感想としては、それ削らなくてもいいじゃないという点もあるが、基本的にはテンプレートコンパイル結果を見て納得という感じだった。

Migration from Vue 1.x」の他に、Vue.js 2 アップデートメモも参考になる。