株式会社リクルートスタッフィング(以下ITSTAFFING)と、株式会社ディー・エヌ・エー(以下DeNA)が共同で開催するDeNAスキルアップイベントが開催されました。
今回のイベントは、前回に引き続き「Vue.js」がテーマ。前回のイベントでは、Vue.jsのMVVM入門編を行いましたが、今回はDeNAの開発現場で使われているVue.jsの使い方を掘り下げて紹介するというものです。
Vue.jsはWeb Componentsと似たコンポーネントシステムを採用しており、再利用可能なパーツを作る仕組みがあります。これをいかにうまく活用するか、そのためのコツが語られました。
Web Componentsとは何か?
今回、講師を務めてくださったのは、DeNAでフロントエンジニアとして新規サービスの開発を行っている遠藤直樹さん。大学在学時からDTPやWebデザインに携わり、ベンチャー企業でデザイナー、イタリアンレストランでシェフを経験した後、Web広告制作会社で3年半勤務。2015年にDeNAに転職し、現在はエンジニアとして活躍しています。
▲株式会社ディー・エヌ・エー 遠藤直樹さん
イベントでは本題に入る前に、Web Componentsとは何かという説明から始めてくださいました。Web Componentsとはひと言で言うと、Webの再利用可能なパーツ作成の仕様のことです。
つまり、
・HTML、CSS、JavaScript3つの専用言語での構成
・HTMLの複雑な入れ子構造
・CSSの意図しない適用範囲
・JavaScriptのDOM構造への依存
といった、これまで再利用可能なパーツを考える際に悩んでいたことを、Web Componentsは解決してくれるのです。
構成するリソースは次の通りです。
1.Custom Elements ユーザーが独自のカスタム要素を定義
2.HTML Imports 断片化したHTMLファイルをロード
3.HTML Template ブラウザネイティブなテンプレート機能
4.Shadow DOM HTMLにスコープを形成
Web Componentsを利用するメリットは複雑な文法構造をなくし、マークアップがすっきりすることと、スタイルの適用範囲を絞り込めること。この2点により作成者側の意図したことが伝わり易くなります。さらに複雑な構造を外部から見えなくすることができることもメリットのひとつです。
しかしながら、以下の表を見ればわかるとおり、すべてのブラウザが対応しているわけではありません。
この仕様はGoogle主導で進んでいるものなので、Google Chromeが対応しているのはよいのですが、ユーザーの多いInternet ExplorerやMicrosoft Edgeでは進んでおらず、ブラウザベンダーによって足並みがバラバラです。
Polyfillもありますが、PolymerやX-Tagなどのライブラリでもプロダクトで使われていることはほとんどありません。
今年で開発4年目となるので、そろそろ仕様として固まりつつあるとは思うものの、現状Web Componentsの採用は難しい。そこで注目したのがVue.jsです。
Vue.jsで再利用可能なコンポーネントを作成する
Vue.jsには次の2つの重要なコンセプトがあります。
・リアクティブデータバインディング
・コンポーネントシステム
Vue.jsのコンポーネントシステムは、Custom Elementsの仕様に沿った構文を提供しています。
ex.
slot 要素 https://www.w3.org/TR/shadow-dom/#slots
is 属性 https://www.w3.org/TR/custom-elements/#attr-is
例えば大規模アプリケーションでもVue.jsなら次のようにわかりやすい構造で作ることができます。
ここまで説明をしたところで、実際にコンポーネントを作成してみることに。作成するのはCodePen。以下のURLからPenをForkします。
hands-on-vuejs-vol2-0 http://codepen.io/naokie/pen/rLwLyd
途中経過を間違って消してしまわないように、ForkするときにログインまたはSave as Anonymousするようにという注意がありました。
前回の成果物の機能についての復習。成果物の概要は次の通りです。
NewTaskというインプット要素にテキストを入れてAddボタンを押すと、新しいタスクを作り、Deleteボタンを押すと、タスクを消すことができます。またチェックボックスにチェックが入り、一番下に残タスクの合計数が表示されています。
この実装テンプレートは次の通りです。
Vol.1 テンプレート
Vol.1 Vue インスタンス
これを基にコンポーネントを作成していきます。
まず考えなければいけないのが再利用可能な単位です。
1.同じページで繰り返し表示する(リスト表示)
2.サイト内で繰り返し利用する(ウィジェット表示)
今回作成するコンポーネントはTodoコンポーネントとAppコンポーネントです。
Todoコンポーネントを作成する
まずはTodoコンポーネントを作成します。
該当するHTML Templateを切り出します。
そして切り出した箇所をカスタム要素で置き換えていきます。
コンポーネントインスタンスはそれぞれスコープを持っているので、このままではTodoコンポーネントは、表示するデータを参照できません。そこで、コンポーネント間のデータを伝達するためにpropsオプションを使います。
Propsを使う
props には、.sync と .onece の2つの修飾子があります。
それぞれ、親子間でデータを同期する two-way バインディングや、一度セットアップしたらその先の変更を同期しない one-time バインディングをすることができます。使用者がコンポーネントを正しく使うことを確実にするため、検証要件の設定も可能です。
これでTodoコンポーネントを実際に作っていくと、以下のようになります。
コンポーネントの作成
Vue.extendと書いてそこにオプションとしてインスタンスを作ったときと同じようなプロパティを設定していくとTodoコンポーネントが完成します。todo というプロパティが渡されなければ、タスクを表示することができないため、 required: true としておきます。
続いてコンポーネントを登録します。todoというタグの名前で、コンポーネントを登録します。todoと言う名前はHTMLの制約上、大文字小文字の区別がありません。ここではアンダースコアやキャメルケースとかは使用できないので、注意してください。
ここまでできたら、以下の4つの動作が正しく動くか、確認をします。
・チェックマークの付け外し
・新規タスクの登録
・タスクの削除
・残タスクのカウント表示
タスクの削除が動作しなくなっているはずです。
タスクの削除についての該当するソースコードは次の箇所です。
<button v-on:click="deleteTodo(todo)">Delete</button>
deleteTodo(todo)というメソッドが、今回作成したTodoコンポーネントには、まだ存在しません。
ここで一度考えなければならないのが、データ操作と表示の役割分担です。
今回、データ(タスクのリスト)は親コンポーネントが持つこととします。データ操作も親コンポーネントの役割です。子コンポーネント(Todoコンポーネント)はユーザーのアクションに対して、イベントを通知するだけとします。すると、親子間でイベントの管理が必要になってきます。
そこで使用するのがVue.jsのカスタムイベントです。これを使うと、コンポーネント間のイベント通信ができるようになります。コンポーネント間のイベント通信の種類は以下の通りです。
・$on() を使用してイベントをリッスンします
・$emit() を使用して自身のイベントをトリガーします
・$dispatch() を使用して親方向にイベントを送出します
・$broadcast() を使用して子孫方向にイベントを送出します
では実際にTodoコンポーネントからイベントの送出をしてみましょう。
続いて親コンポーネントでイベントをリッスンします。
データ操作の処理内容は、前回作成したインスタンスメソッド(deleteTodo)と同じなので、そのままコピーしてしまいます。
ここまででうまく動かなかった場合は、以下のURLに成果物が置いてあるので、ぜひ、見比べて間違いを正してください。
・hands-on-vuejs-vol2-1 http://codepen.io/naokie/pen/qNjNAw
Appコンポーネントを作成する
次はAppコンポーネントの作成方法についてです。Appコンポーネントは小さなアプリ(ウィジェット)をコンポーネント化するというもの。手順はTodoコンポーネントを作成した時と同様で、まずはテンプレートを切り出すところから始めます。
続いて切り出した箇所をカスタム要素で置き換えます。ただし、このままだとカスタムタグ1個に対して、4つのタグで構成されてしまいます。そこで1つのカスタムタグに1つのタグにするために、ラップするようなタグを用意し、コンポーネントを作成します。
続いてコンポーネントを登録し、先と同様に再び動作の確認を行います。
すると、残タスク数の表示や新規タスク登録の動作がおかしくなっているので、修正を行います。
まずは残タスク数表示の修正から。算出プロパティremainsはAppコンポーネントのスコープで閉じているので、親コンポーネントの中ではなく、Appコンポーネントの中から使えるように修正します。
続いて新規タスク登録の修正を行います。データ操作は親コンポーネントが持っていた、addTodoの処理内容をAppコンポーネントの方にそのまま持ってきて、Appコンポーネントからイベントを送出するようにします。
データの操作は親コンポーネントに任せます。
newTaskは親コンポーネントにdataプロパティを定義していましたが、それを消し、Appコンポーネントに必要なdataプロパティとして移植します。
ここまで書くと、親コンポーネントにあったmethodsプロパティが一切なくなりました。親コンポーネントはデータを保持してイベントに対して、そのデータを操作するだけの構成になっています。
修正が終わったら再度、動作を確認します。これでAppコンポーネント完成。再利用可能なパーツも以下のように完成しました。
ここまでの成果物は以下のURLにあるので、うまく動かなかった人は確認してみましょう。
・hands-on-vuejs-vol2-2 http://codepen.io/naokie/pen/oLwLAj
最後はNewTaskコンポーネントを作成する演習
最後はNewTaskコンポーネントの作成を実際にやってみることに。先ほど作ったNewTaskをnew-taskというカスタムタグで使えるようにするという演習です。
テンプレートの切り出しの方法について説明をした後、参加者は先ほど、習った内容を元に各自プログラミングします。
途中でつまずいたり、プログラミングが完了してもうまく動作しないことがあっても、DeNAの皆さんが優しくサポートしてくれるので、安心してハンズオンに取り組むことができました。
プログラミングができたら、先ほど同様の確認作業を行います。講師の遠藤さん、およびDeNAのみなさんがテーブルをまわり、確認作業まで進んだ人が増えたところで、遠藤さんが正解を披露しました。
CodePenには今回の勉強会で使ったすべてのコードが保存されているので、わからなかったところの確認や復習にぜひ活用してほしいと遠藤さんは呼びかけ、ハンズオン勉強会は終了しました。
DeNAとITSTAFFINGではこのようなハンズオン勉強会を定期的に開催しています。
フロントエンジニアとしてスキルアップしたいというみなさん。ぜひ、次回の勉強会に参加してみてはいかがでしょうか。