本連載は分散型マイクロブログ用ソフトウェアMisskeyの開発に関する紹介と、関連するWeb技術について解説を行っています。
以前から紹介していますが、MisskeyはフロントエンドのUIフレームワークとしてVue 3を採用しており、そのVueの新しい機能であるComposition APIも活用しています。
今回はそのComposition APIについて解説します。
Composition APIとは
簡単に言うと、Vueのコンポーネントには以下の2通りの書き方があります。
- Options APIを使った今までの書き方
- Composition APIを使った新しい書き方
Composition APIのほうが新しいですが、現時点では従来のOptions APIが非推奨になったりはしておらず、Vueのドキュメントを見てもどちらも同じくらい
(個人的な意見では、書き方が複数混在するのは混乱を招くため、古いOptions APIを早く廃止または非推奨にして、より柔軟に扱えるComposition APIに一本化したほうが良いように思いますが、互換性やユーザー層を考えるとなかなかそういうわけにもいかないのかもしれませんね。)
Composition APIを使用すると、関連する処理を一箇所にまとめて記述できるようになり、読みやすいコンポーネントが作成できるほか、ロジックを再利用可能な形に抽出してより柔軟にコンポーネントを書くことができます。
Misskeyではすべてのコンポーネントについて、Composition APIへの移行が完了しています。
Composition APIの例
複数のロジックをもつあるコンポーネントに、
それを実現するには、コンポーネントがインスタンス化された時にwindow.
を呼び出してテキスト更新処理を登録し、コンポーネントが破棄された際にはwindow.
を呼び出して更新処理を解除する、というようになります。
この一連の登録・
export default {
data() {
return {
text: null,
intervalId: null,
}
},
mounted() {
this.intervalId = window.setInterval(this.changeText, 1000)
// ... そのほかに必要な何らかの処理(1) ...
},
unmounted() {
window.clearInterval(this.intervalId)
// ... そのほかに必要な何らかの処理(2) ...
},
methods: {
// ... そのほかに必要な何らかの処理(3) ...
changeText() {
this.text = getSomething()
}
}
}
登録処理・
それに対して、Composition APIを用いた場合以下のようになります。
const text = ref(null)
let intervalId = null
function changeText() {
text.value = getSomething()
}
onMounted(() => {
intervalId = window.setInterval(changeText, 1000)
})
onUnmounted(() => {
window.clearInterval(intervalId)
})
// ... そのほかに必要な何らかの処理(1) ...
// ... そのほかに必要な何らかの処理(2) ...
// ... そのほかに必要な何らかの処理(3) ...
ref
というのはリアクティビティを実現するためのラッパーで、これによって値の変更がDOMにリアルタイムで反映されるようになります。実際の値は.value
で参照します。
注目してほしいのは、登録処理・
さらにComposition APIを使用する場合、必ずしもロジックをコンポーネント内に書く必要がありません。
どういうことかというと、これら一連のロジックを別の関数に抽出することができ、その関数を別ファイルからimportする形にもできるということです。
export function useInterval(fn, interval) {
let intervalId = null
onMounted(() => {
intervalId = window.setInterval(fn, interval)
})
onUnmounted(() => {
window.clearInterval(intervalId)
})
}
上記のファイルをコンポーネントでimportして使うようにすると、以下のようになります。
import { useInterval } from '...'
const text = ref(null)
useInterval(() => {
text.value = getSomething()
}, 1000)
// ... そのほかに必要な何らかの処理(1) ...
// ... そのほかに必要な何らかの処理(2) ...
// ... そのほかに必要な何らかの処理(3) ...
このようにロジックを別ファイルに抽出することで、複数のコンポーネント間で共通するロジックを再利用することも可能になります。
また、このようにコンポーネントから抽出した関数は慣例的にuse〇〇という名前にされることが多く、汎用的な様々なuse〇〇が集められたライブラリも公開されています。
これらの
そしてこのようなAPIスタイルおよび設計思想は、Vueに限らず最近のフロントエンドフレームワークでのトレンドになっており、今後主流になっていくものと思われます。
マクロ(旧 Reactivity Transform)の利用
Composition APIに含まれるリアクティビティAPIは、マクロ
ノート:以前はReactivity Transform
この経緯はRFCに残されていますので、興味のある方は見てみてください。
マクロを使用すると例えば以下のように書けます。
let text = $ref(null)
function changeText() {
text = getSomething()
}
リアクティブな変数を定義する際、ref
の代わりに$ref
が使われ、値を参照する際は.value
と記述する必要がなくなっています。
仕組みとしては、Vueのコンパイラが$ref
を見つけると、通常のref
を用いたコードに書き換えます。つまりマクロやシンタックスシュガーのようなもので、ランタイムで何か特別なことをするわけではありません。
ただし、前述したようにVueコンポーネント内でしか使えず、それ以外の場所での記述と一貫性がなくなるといったデメリットもありますので、必ずの使用が推奨されるものではありません。
Options APIの削除
プロジェクト内でComposition APIのみ使用する場合、Vueコンパイラの__
フラグをfalse
にすることで、ランタイムから完全にOptions APIのためのコードを削除できます。
Viteなら以下のように設定します。
export default {
// 中略
define: {
__VUE_OPTIONS_API__: false
}
}
これによりバンドルサイズをいくらか削減できます。しかし、自分でOptions APIを使用していなくても依存しているライブラリ内でOptions APIが使われている部分が残っていたりする場合は、__
をfalse
にできないので注意しましょう。
MisskeyのコードベースにおいてもOptions APIの使用箇所は残っていませんが、依存するライブラリがまだOptions APIを使っているため、バンドルからOptions APIを完全に削除するまでには至っていません。
Misskeyでの活用
MisskeyではすべてのコンポーネントについてComposition APIへの移行が完了しているのは先に述べた通りです。
ただそれだけではなく、Composition APIがコンポーネントの外でも使えるという利点を活かし、クライアント全体で使うステートの管理も行っています。
管理するステートにはアカウント情報やクライアントの設定情報などが含まれ、ステートをリアクティビティAPIのreactive
を使用することで、それらの情報の変更をリアルタイムでクライアント上に反映させることが可能です。
このようなステート管理は、Vue公式でpiniaといったライブラリが提供されていますが、Misskeyでは
このほかにも、例で説明したようなuse
関数も活用していて、Misskey固有のものでいうと
まとめ
今回はVue 3から標準で使えるようになったComposition APIについて紹介しました。
Composition APIを使用することで、コンポーネントのリーダビリティが向上するほか、より柔軟にロジックを記述できることを説明しました。また、それらのロジックを関数として抽出し、複数のコンポーネント間で再利用可能になることも特徴に挙げました。
他にも、Composition APIはTree-shaking
Options APIは現時点では続投されていますが、Composition APIの方が後発であり優れている点が多いことから、将来的には非推奨になることも十分予想できますので、まだOptions APIを使用している方は早めに移行することを推奨します。