Vue.jsでアプリケーションを実装する際のTips

田原一樹氏(以下、田原):では始めさせていただきます。みなさんこんばんは。

今、御覧頂いているスライドの表紙で出落ち感が半端ないんですが、こちらの内容について本日はお話させて頂ければと思います。

改めまして、私、田原と申します。株式会社レアジョブで現在App・UXチームに所属しており、フロントエンドエンジニアとして開発を行っております。

今日で、レアジョブに入社して半年ぐらい経ちますが、これまで、JSフレームワークはReactをメインでさわっておりました。今はご覧の通り、レアジョブでVueを使用しております、宜しくお願い致します。

先程のタイトルにありましたが、本日のお話ではVueでのアプリケーションを実装する際にMixinを利用することについての注意点だったり、Mixinに変わる代替案について、お話できればと思っております。

本題の前にまずは少しだけ、弊社レアジョブについてのご説明をさせて頂きたいと思います。

弊社レアジョブは英会話を通じて、すべての人に平等なチャンスを、日本人の1000万人の方が英語を話せるようにするということを理念にして掲げております、Edtech企業です。

弊社が提供するサービスの中には英語力向上のアプリであったり、実際に生徒の方に教室に来校していただく塾のような形式をとっているサービスがあるのですが、私が現在メインで携わっているのが、レッスンルームというオンライン英会話の事業になります。

具体的にはオンライン上で行うビデオ通話を通して外国人の先生と会話・レッスンを実施して頂き、ユーザーの英語力向上に向けたサービスの開発を担当しております。

内容と致しまして、現在ネイティブアプリ(ios / android)からの受講とWebブラウザからの受講が可能となっております。

これまではビデオ通話を担う部分にSkypeを利用しておりましたが、Webブラウザ版ではこれをWebRTCという技術とおなじみのVue.jsに置き換えて、Webブラウザを開くだけで、ビデオ通話が可能になる仕組みを実装しております。

今日の勉強会のもう1つのテーマであるLaravelは使ってないの? という疑問が上がってきそうですが、Laravelは、このレッスンルームのプロダクトが派生した社会人向け、高単価のちょうどRIZAPのような感じの成果保証型英会話サービスで使用しております。

Mixinとその代用案について

説明が長くなりましたが、それでは本題に戻らせていただきます。

Mixinの話とその代替案についてです。本日、勉強会に参加されているみなさんは日々、Vueを使用し開発を行わていると思いますので、既にご存知だと思いますが、念の為に「Mixinってそもそもどんな機能?」ということで、改めてVueの公式から説明を引用させて頂くのですが、Mixinとはコンポーネントに再利用可能な、柔軟性のある機能を持たせるための方法になります。

Vueコンポーネントに対して、メソッド等をMixinと定義することで、柔軟にコンポーネント側から呼び出せる機能になっております。

こちらのサンプル画像が先ほど、説明させて頂きましたレッスンルームをブラウザで見た画面になっておりまして、おおまかではあるのですが、便宜上、青い枠線で囲んでいる箇所毎にコンポーネントが区切らております。

基本的に挙動等の機能郡と各コンポーネントが紐付くような形でstore modulesとcomponentを作成しております。

このvideo.tsというstore modulesファイルを例に挙げますと、ビデオ通話を担うこの領域がVideoのcomponentになるのでこのcomponentと先ほどのvideo.tsが紐付き、対になるように実装をおこなっております。

今、ご説明した通り、機能とコンポーネントを対になるように実装していても、開発を進める中で、別のコンポーネント、または全てのコンポーネントでこの機能を使いたということが、往々にしてありあります。

レッスンルームの画面を例にとりますと、このWebアプリケーションルーティングがほぼ存在しておらず、およそ1つの画面構成で完結してしまうものであり、基本的にメインの画面のみで可動しているプロダクトになっている為、コンポーネントを横断して共通して機能等を使いたいということが発生しました。

そこで弊社では横断的にstore内のmethodやdataにアクセスできるようにするためにMixinを多様しており、コンポーネントをどこからでもその対象となるメソッドを呼べるようなstoreを実装しております。

やってみて何が起きたか

実際にこれをやってみて何が起きたかというと、Mixinで定義しているmethodないしactionないしmutationがComponent内でいきなり出てくる感じがあります。

基本的にComponentとstoreの機能で対になる作りなので、「このComponentは命名からするとこのstoreと対だなぁ」とあたりをつけていても思っていても、Component内、下部定義にMixinでimportされており、「実はglobalの方で定義されていたのか……」ということが生じており、どこで定義されているかわからない問題がありました。

また、globalで利用する為にMixinで定義しているにも関わらず、通常のstoreをcomponent内で呼び出せるようにするためのMapMutationsであったり、MapGettersであったりに定義しているようなことがある為、記述の方法が混在しております。

globalでの利用の際にはMixinを使用しているのですが、先程のように他の方法で定義されてしまっている箇所もあるため、どれがMixinとしての定義のものであるのか、prefixを付けようにもうまく付けられないような状態になっていました。

これに関しては、ルール設定と統一の不十分さによるものなので、何とも言えないことではあります。

更にMergeストラテジの問題で、同一名のメソッドや実行順序で競合が起きてしまって意図しないメソッドが発火してしまうというようなことがありました。これまでの実装箇所については特に問題がなかったのですが、追加機能を付与して実装する場合に「あれ?」っていうところが多少ありました。

「駄目だ……早くなんとかしないと……」とメンバーにも言われて、今検討しているところであります…

storeのPlugin化

そこで現在、代替案として検討しているものの中にstoreのプラグイン化ということが候補としてあります。Vueのそもそもの機能としてプラグインという機能があり、そのカスタムディレクティブに別途、グローバルに呼びたいstoreを追加すれば、Mixinとわりと同じようなことができるんじゃないかということで、現在検討しているところです。

それがコチラです。

(実際のコードを見ながら)

めちゃめちゃシンプルなVueのチュートリアルのような画面ですが、こちらがplugin countというpluginを使って実装したstoreです。そしてこちらがMapGettersやMapMutationsでいちいちインポートしている画面です。そしてこちらがこれまでの説明にありましたMixinで定義しているものです。

中身のコードがどうなっているかというと、これが1番目のpluginで、2番目がMapperのもので、3番目がMixinになります。

どのように定義しているかというと、mapperのものはこのsampleというstoreのモジュールからcountというものを抜き出して、ここで定義しております。ソースの中身はこんな感じです。

次にMixinのほうですが、Mixinのほうは、Mixin.jsというところに、Gettersで定義を行い、それをstoreにしてインポートしております。Mixin自体は別ファイルでMixinしております。

これらの設定方でも良いのですが、最後にpluginを使った方法についてです。storeの設定自体は普通のstoreの設定と変わらず、もう1つstoreを準備していただいて、アプリケーションRootでpluginの方のstoreをインポートしてきて、installのカスタムディレクティブにこのstoreを登録します。

こうすることでグローバルからのpluginということで、ここの命名がplefix$のような機能になって、各コンポーネントから呼べるようになります。

これはMixinと同様で、コンポーネントとどこからでも呼べるのと、Mixinのように定義しなくてもグローバルで使いたい場合は、最初にインポートだけしてあげれば使えることがメリットになっております。

挙動としては、同じようなstoreを書いているので同じような機能が実行できます。

Pluginによる恩恵

今、説明させていただいた通りですが、別のstoreとして切り出して登録しておけば、mixinのようにコンポーネント毎に定義しなくてもどこからでも呼ぶことができます。カスタムディレクティブを付与する際にplefix を$○○のような名称で付けます。prefix以降は自由でいいので、このprefixがついているのでどれがglobalのメソッドを呼んでいるものなのか判断しやすくなり、Component内にMap○○を定義しないので、コンポーネント内の記述量が増えないということがあります。mapでインポートしてくると意外にもコード量が増えてしまうので、そこは好き嫌いになりますが、見通しをよくしたい方に関してはオススメです。

最後にメリットとしてMixinに関してはコンポーネント単位でメソッドを定義すればOverrideできます。

pluginに関しては、これまで説明させて頂いた通りglobalをmixin同様に呼べたり、Map○○の定義等の割愛などですね、それに同一メソッド名でもplefixのあるかたちで切っているので競合は起きません。

Mixinのデメリットとしては、いっぱいMixinすると複雑化していきやすいです。冒頭でも説明させていただいた通り、Mergeストラテジに気をつけないと意図しない挙動になってしまうことがあります。

pluginに関しては、VueのRootに登録しておけばMixin同様どこからでも呼べるようになりますが、各コンポーネント内でstore内のデータを参照する際は一度、component内のcomputedで値をgetしてから反映させるようにするしかないので、書き方には注意が必要です。

まとめと致しまして、Mixinは時と場合を見て使ってあげたほうが良いのではということとglobalで使うstoreのみ、プラグイン化することでどこからでも呼べるようになるのは便利だなぁっと思っています。プラグイン化した時にコンポーネントにイベントを仕込む際にもうちょっといい方法がないかは今後も探っていく予定です。

ご清聴ありがとうございました。

(会場拍手)