Vue.jsの次メジャーバージョン(v3)が2020年Q1にリリースされる。特に目新しいのが、Vue Composition APIという、これまでのVueの書き方とは違う関数ベースのAPI。
これに伴って、Vue.jsとReactの選定基準についても改めて考えないとなぁと思い書いた。
今回はまず新しいVue Composition APIに触れて、最後にReactとの違いについて書いてみる。
TL;DR(忙しい人向け)
- TypeScript前提ならReact
- TypeScriptを使わない(or 使えない)規模ならVue.js
- 既存の大規模Vue.jsプロジェクトは、徐々にComposition APIに移行すると幸せ
Vue.js 3の Composition API とは
Composition APIでは、下のコードのように、reactive
やcomputed
といった関数を用いて組み立てていく。見て分かる通り、今までとは全く違う、React Hooksと近しい書き方になる。
<template>
<button @click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
</button>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
count: 0, // data: { count: 0 } と同じ
double: computed(() => state.count * 2) // computed: { double: () => { this.count * 2 } } と同じ
})
// methods: { increments() { this.count++ } } と同じ (下でsetupからreturnしているため)
function increment() {
state.count++
}
// template内で使いたいものを返す
return {
state,
increment
}
}
}
</script>
このComposition APIを触ってみた時は興奮したんだけど、冷静になって考えてみると、Vue.jsの存在意義ってなんだろうとふと思った。
なぜVue.jsにComposition APIが実装されたか
Composition APIのRFCでも書かれているが、このAPIが誕生した理由は2つある。
https://vue-composition-api-rfc.netlify.com/#motivation
- ロジックの抽出と再利用性
- TypeScript(型推論)の改善
ロジックの抽出と再利用性
VueはComponentの分割手法として、Mixinを提供していたが、Mixinでは2つの問題があった。
Mixinの1つ目の問題は**「Mixinにどのようなメソッドなどがあるかを、Mixinを利用する側から一見して分からない」**こと。
export default {
mixins: [myGreatMixin] // mixinが何を提供しているかはファイルを見ないと分からない
}
Mixinの2つ目の問題は、**「Mixinの中でしか用いないメソッドなどをprivateにすることができない」**こと。
Vue.jsのスタイルガイドでは$_mixinName_methodName
のようにprefix付きで命名することを強要している。
const myGreatMixin = {
methods: {
publicMethod: function () {
console.log('Hello from public')
},
$_myGreatMixin_privateMethod: function () { // 親からも呼べてしまう
console.log('Hello from private')
}
}
}
これら2つの問題により、Vueが提供しているMixinは、可読性やメンテナンス性において良いものとは言えない。Mixinの中でさらにMixinを使う場合なんかは、正直書いていて楽しくない。
それに対しVue Compostion APIでは以下のように書くことができる。
export default createComopnent({
setup() {
const { hello } = useSayHello()
onMounted(() => {
hello() // 'Hello'
})
}
})
// lib/say-hello.js
export const useSayHello = () => {
const hello = () => {
console.log('Hello')
}
return { hello }
}
比較して分かる通り、Composition APIではconst { hello } = useSayHello()
といった具合に、抽象化されたコードがどのようなメソッドやデータを提供しているかひと目で分かる。またプライベートなデータもreturn
しない限りスコープを閉じることができる。
2. TypeScript(型推論)の改善
これは現状、Vue.jsとTypeScriptの相性が悪いという問題を抱えているためである。
例えばVue.extend()
内で、mixinが提供するメソッドなどに対して型推論する場合は、以下のように、そのmixinが提供するメソッドなどを全て個別に型定義しなければいけない。
declare module 'vue/types/vue' {
interface Vue {
items: Item[] // 「itemsというdataを提供するmixin」のための定義
setSnackbar: (message: string) => void // 「setSnackbarというメソッドを提供するmixin」のための定義
}
}
これでは、Mixinとは別に型を二重定義する必要があり、本来バグを防ぐためのTypeScriptが逆にバグの温床になってしまう。しかしComposition APIは純粋な関数であるため、特に工夫をせずとも型推論ができる。
TypeScriptデコレータによる推論は廃止
一時期、Vueをvue-property-decoratorなどのTypeScriptデコレータを使って型推論をするという流れがあり、Vue 3でもクラスベースのAPIを公式に提供しようかという話が挙がっていた。
しかしTypeScriptデコレータ自体まだexperimental(実験的)な機能であるため、その話は議論の末に捨てられてしまった。
詳細はIssueにある。[Abandoned] Class API proposal by yyx990803 · vuejs/rfcs · GitHub
Composition APIの登場
以上の諸問題によって、Vue.js は関数ベースのComposition APIを提供することを策定した。
Vue.js 2でもComposition APIを試せるようにプラグインを提供している。
https://github.com/vuejs/composition-api
Vue.jsとReactはどちらを選ぶべきか
早速本題に入ってみる。
TypeScriptならReact
まず、「はじめからTypeScriptで書く」というプロジェクトにおいては、Reactを選択するべきだと思う。Vue.jsであればComposition API自体がまず正式リリースされていないし、バージョン2の書き方は先に挙げたTypeScriptとの相性問題がある。それなら安定したReact Hooksを選ぶのが間違いない。
ではComposition APIが正式リリースされた際はどうすればいいか?。少し悩ましいがこれも自分の答えはReact。Composition APIから後発ならではの良さというのも特に感じなかったし、vuexやvue-routerといった周辺ツールに関しては、概念はReact系と一緒なのでそれほど学習コストに差はない。
またReactのコミュニティとの差を感じることもたまにある。例えばマテリアルデザインのUIライブラリにおいて、Vue.jsベースのVuetify.jsよりもReactベースのMaterial-UIがライブラリとして完成度が高い。他のライブラリのケースも、ほぼReactの方が安定して開発されている場面によく会う(2021年はそれほど変わらなくなると思うけど)。
いつVue.jsを使うべきなのか
Vue.jsは、TypeScriptやReactの習熟度が高くないチームであったり、小規模なケースにおいて採用することで強みを発揮できる。
そもそもVue.jsは、親しみやすいインタフェースである程度の複雑なアプリケーションを作れる、といった点がユーザーに愛されていた点だと思っている。Mixinを使わなければVue.jsは読みやすく、それほどJavaScriptに精通していなくとも書くことができる。
またSSRアプリケーションのフレームワークとしてReactのNext.jsよりも、Vue.jsのNuxt.jsの方が予め用意してくれている領域が広く、サクッとモダンなアプリケーションを作ることができる。
そういった点でも、Vue.jsはReactよりも小規模なプロジェクトに向いている。
いつComposition APIを使うべきか
Composition APIは、既にVue.jsを導入しているプロジェクトで有用な選択肢になる。
というのも、Vue2の記法自体は3でも使うことができ、Composition APIとも併用して動かすことができる。そのため、今現在運用しているVue 2のプロジェクトがTypeScriptを欲しくなるような規模になれば、部分的にComposition APIへ移行し、TypeScriptの強化とコードの抽象化を行うことができる。
所感
2020年の選定基準について、一通り書いてみた。
Vue.jsが型推論を考慮せずに設計されたこともあり、JavaScript界隈において少し曖昧な立ち位置になっているなぁと感じる。こういったVue.jsのAPIの拡張はメリットもある一方、ユーザーにとって選択疲れを引き起こしてしまっている。
もういっそのことTypeScriptを捨てて、バージョン2の書き方で貫き通してもよかったんじゃないかなぁと、少し思った。