東京上野のWeb制作会社LIG仕事も遊びもいいオフィス。フロントエンドシェアこんにちは。フロントエンドのつっちーです。以前は最先端感をプンプンさせていたSPA(シングルページアプリケーション)ですが、最近では事例や情報が増え、当たり前に選択される技術となってきたように感じます。今後もどんどん使われていくのでしょう。いってサーバーサイドテンプレート(Haml、Slim、テンプレートとしてのPHPなど)やHTMLファイルへのマークアップが無くなるわけではありません。SEO対策、CMSやフレームワークへの適応など、様々な理由でまだまだ使われていくはずです。案件でも実際に使用し、大きなメリットを感じられたため、記事にまとめておこうと思います。Vue.jsのSPAでの用法であれば、他にいくらでも参考資料が存在するため、この記事では「サーバーサイドテンプレートやHTMLファイルへのマークアップに対して、Vue.jsを使用する方法」に的を絞ります。言い換えると、「コンポーネントを使用せず、既に存在するDOMにVueインスタンス(後述)をマウントする用法」です。これまでjQueryを利用していたシーンにマッチする用法、とも言えるのではないでしょうか。いうわけで、「サーバーサイドテンプレートやHTMLファイルへのマークアップに対して、Vue.jsを使用する方法」について、これから数回に渡ってお届けします。今回は、jQueryのよくあるパターンを、Vue.jsではどう記述できるのかを見ていきましょう。この連載では、SPA制作においてはVueのコンポーネント機能を用いて実装すべき部分を、Vueインスタンスとして実装します。Vueコンポーネントは、JS(あるいはVue)ファイルにテンプレートを記述することとなり、この連載のテーマに沿わないためです。サーバーサイドテンプレートやHTMLファイルへのマークアップが不要な部分もあるでしょう。SEOともデータとも無関係の内容を持ったパーツがこれにあたります。この連載では触れませんが、そのようなパーツが、Vueインスタンスでマウントされた要素の中に複数回出現する場合は、Vueコンポーネントの使用も検討してください。入力に応じてデータを変更するまとめjQueryをVue.jsに書き換えてみる上記目次にある8つのパターンについて、jQueryとVue.jsとを比較していきます。以下の例では、CodePenの機能によって、あらかじめ下記のscriptタグが挿入されていることにご注意ください。src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>1行目でjQueryを、2行目でVue.jsをそれぞれ読み込んでいます。Modulesのimportと、Webpackなどのモジュールバンドラを用いた読み込みでも、同様に動作します。利用しているバージョンは下記の通りです。2.3.4この連載においては1Vueインスタンスを1部品として扱います。SPA制作における、1Vueインスタンス=1アプリケーション、1コンポーネント=1部品の扱い方とは異なることにご注意ください。以下の例は、Vueインスタンスとしては小さすぎるかもしれません。Vueインスタンス(後述)は、データ(後述)やメソッド(後述)の共有範囲であることを意識して利用してください。適切な大きさの一例として、次回の記事では、モーダルウインドウを1Vueインスタンスとして実装する予定です。CodePen.クリックでアラートが表示されるボタンの実装です。Vue.jsの処理は、まず既存のDOMをマウント(Vue.jsが生成するDOMで置き換え)するところから始まります。マウントは、new演算子によってVueコンストラクタからVueインスタンスを生成するときに、実行されます。コンストラクタの引数にはオプションオブジェクトが渡され、その内容によってVueインスタンスの挙動の大部分が決まります。オプションオブジェクトのel属性には、マウント対象要素のセレクタを設定します。これから説明していくVue.jsの構文は、マウント対象要素自体とその子要素でのみ、有効となります。オプションオブジェクトのmethods属性には、1つ以上の関数定義を含むオブジェクトを設定します。このオブジェクトが持つ関数(メソッド)へは、テンプレートやオプションオブジェクト内からの参照が可能です。テンプレートで使用する、Vue.js独自の特殊なHTML属性を、ディレクティブと呼びます。ディレクティブは通常のHTML属性と異なり、値にJavaScript式を必要とします。形で使用し、値の関数をイベントに対するリスナとして登録することができます。CodePen.クリックでテキストが切り替わるボタンの実装です。2つの要素の表示/非表示を切り替えることで実現しています。オプションオブジェクトのdata属性には、自由な内容のオブジェクト(データ)を設定します。データの属性へは、テンプレートやオプションオブジェクト内からの参照が可能です。データの属性の持つ値が変化すると、そのデータを参照するすべての箇所に変更が反映されます(リアクティブシステム)。テンプレートからは、メソッド名/データの属性名で、メソッド/データを参照できます。オプションオブジェクト内からは、this.メソッド名/this.データの属性名の形で、メソッド/データを参照できます。v-showディレクティブは、値がtrueのときはその要素を表示し、値がfalseのときはその要素を非表示にします。CodePen.クリックでテキストが切り替わるボタンの実装です。上の実装とは異なり、2つの要素の挿入/削除によって実現し、ボタン自体のテキストも切り替えています。v-ifディレクティブは、値がtrueのときにはその要素を挿入し(存在させ)、falseのときにはその要素を削除します。v-showディレクティブとは、その要素が削除されることはない点で異なります。オプションオブジェクトのcomputed属性には、1つ以上の関数定義を含むオブジェクトを設定します。この関数定義は算出プロパティと呼ばれ、データと同じ方法で、テンプレートやオプションオブジェクト内からの参照が可能です。算出プロパティは、他のデータの値に応じて変動する値を持つデータとして使用します。算出プロパティを参照すると、その時点で値の関数を実行して返ってくる値が格納されています。値の関数は、必ず値を返している(returnで終わる)必要があります。テンプレートにおいて、二重の波カッコでJavaScript式を囲うと、その箇所にその式の値が展開されます。CodePen.クリックでinputの入力可/入力不可を切り替えるボタンの実装です。v-bindディレクティブは、v-bind:HTML属性名の形で使用することで、値のVueインスタンスのデータをHTML属性と結びつけることができます。:HTML属性名は、v-bind:HTML属性名の省略記法です。CodePen.クリックでテキストの色が切り替わるボタンの実装です。要素のクラスを切り替えることで実現しています。class属性とVueインスタンスのデータとを結びつける場合は、ディレクティブの値としてオブジェクトを利用できます。キーはクラス名を表し、値がtrueのときにはそのクラスが有効となり(設定され)、falseのときにはそのクラスが無効となります。カンマ区切りで、複数のクラスについて設定が可能です。CodePen.クリックでテキストの色が切り替わるボタンの実装です。上の実装とは異なり、要素のスタイルを切り替えることで実現しています。style属性とVueインスタンスのデータとを結びつける場合も、ディレクティブの値としてオブジェクトを利用できます。キーはCSSプロパティ名を表し、値がそのCSSプロパティの値として設定されます。カンマ区切りで、複数のスタイルについて設定が可能です。CodePen.クリックでテキストの色が切り替わるボタンの実装です。クリック時のイベントオブジェクトを通して、要素の属性を読み取り、色を設定し分けています。ディレクティブの値に関数を設定する場合、実行の形をとることで、関数に任意の引数を渡すことができます。イベントリスナの引数には、イベントオブジェクトを参照する$eventを利用することができます。CodePen.値を変更することで、自動的に2倍の数値を表示するinputの実装です。v-modelディレクティブは、input要素とtextarea要素に使用できるディレクティブで、値のVueインスタンスのデータと要素のvalue属性とを結びつけることができます。Vueインスタンスのデータ変更がvalue属性に反映され、逆に、value属性の変更もVueインスタンスのデータに反映されます。Validatorに通すと、その箇所に関してエラーが出力されます。これは、Vue.jsの構文がHTML標準の構文ではないためです。Validatorは任意のエラーをフィルタリングする機能を提供しています。(詳しくはこちらの投稿をご参照ください)Vue.jsの構文が含まれたHTMLをValidatorに通す時は、こちらの機能を利用しましょう。まとめいかがでしょうか。Vue.jsでは、JSに使用するデータや処理の一覧が記述され、HTMLにどの要素がそれらを利用するのかが記述されます。これにより、JSではDOM選択とイベントリスナ登録の記述がなくなり、HTMLではどこで何が起こるかがわかりやすくなっています。次の記事では、このメリットをより感じられるよう、さらに実用に近い例を用いて機能を紹介していきます。