フロントエンド開発と言えばJavaScriptのフレームワークが熱い!
React・Vue・Angularが有名で、聞いたことがある人もいるのではないでしょうか。
BRISKではまれにVueを使う案件に遭遇します。
筆者も触ったことがあるのですが、理解が曖昧でしたので復習がてらまとめたことをブログにしました。
Vueはバージョン3が登場していますが、2系の資料が多い点とバージョン2からバージョン3と段階を踏んだほうがいいかと思いバージョン2の公式ドキュメントを参考に説明していきます。
目次
概要
まずはVue.jsについて概要を説明したいと思います。
Vueとは
Vueはユーザーインターフェースを構築するためのプログレッシブフレームワークです。
どういうことなのでしょうか。
まずユーザーインターフェース(UI)はユーザーとコンピューターの接点すべてを指します。分かりやすい例で言えばエアコンやテレビのリモコンですね。リモコンを操作することによってコンピューターに情報を伝えることができます。
WebサービスではWebブラウザで表示されているサイトがUIと言えます。
次にプログレッシブフレームワークですね。
フレームワークとライブラリという単語は度々目にします。違いを以下にまとめてみました。
開発に必要となる基本機能が提供された枠組み
ライブラリ
再利用可能なコードの集まり。
話を戻すと、プログレッシブフレームワークは直訳すると進行性の枠組みです。
Webサービスは作って終わりというものではないですよね。改修や新規機能の追加など当初より進歩していきます。
その段階で最初に導入していたライブラリ・フレームワークでは要件を満たせないというケースが発生する可能性があります。その場合ライブラリ・フレームワークを選定し、サービスを作り直さないといけないという結構悲惨なことになるかもしれません。
Vueは最初は小さく導入することができ、プロジェクトが大規模になってもツールやライブラリを導入することで対応できる拡張性の高い、まさにプログレッシブフレームワークなのです。
Vueの特徴
次はVueの特徴についていくつか紹介します。
リアクティブなデータバインディング
リアクティブなデータバインディングと見て「そういうことか~」と理解できる人は凄いなと思います。
まずデータバインディングとはデータと対象を結び付け、データあるいは対象の変更を暗示的にもう一方の変更に反映する仕組みのことです。
Vue.jsはDOMとJavaScriptのデータがデータバインディングによって同期しています。
JavaScriptでHTML・CSSを操作できる仕組み
データの変更があれば自動的にDOMが更新され、画面の表示内容が書き変わります。
コンポーネントシステム
Webアプリケーションなどはパーツを組み合わせて表示します。
Vueはパーツをコンポーネント化することによって再利用性を高め、開発効率を高めます。
仮想DOMによる高速レンダリング
まずレンダリングとはデータ記述言語やデータ構造で記述された抽象的で高次の情報から、コンピュータのプログラムを用いて画像・映像・音声などを生成する
ことです。
つまりWebサイトで言うと、Webブラウザでコンテンツを表示することですね!
サイトのレンダリングをもっと詳しく知りたいという方はこちらの記事を読んでみてください。
ReactやVueは仮想DOMを採用しています。
DOMツリーをJavaScriptのオブジェクトとして管理したもの
仮想DOMは変更した差分のみをDOMに反映するので、レンダリングコストを抑えることができます。これによって高速なレンダリングを実現しています。
仮想DOMについてはこちらの記事が有名です。詳細が知りたい方は読んでみてはいかがでしょうか。
導入方法
Vueを導入する方法が4つあります。好きなものを選んでください。
学習のためならCodePenやCodeSandboxでWebブラウザ上で気軽にコードを書くことができます。
このブログではCDNを読み込んでいます。
CDNを読み込む
学習用ならこちらをbodyタグ終わりの直前に読み込めばOKです。
1 2 3 | <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> |
本番環境のCDN読み込みはこちらです。特定のバージョン、ビルド番号を記述しておきましょう。
1 2 3 | <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script> |
JavaScriptファイルをダウンロードして読み込む
こちらからファイルをダウンロードし、bodyの閉じタグ直前に読み込んでください。
開発バージョンと本番バージョンがあるので、環境によって読み込むファイルを変更します。
npmでインストール
大規模アプリケーションを構築する際は、npmでインストールするのを推奨されています。
Node.jsのパッケージを管理するもの
Node.jsをインストールすると自動でついてくる
プロジェクト内で以下のコマンドを叩いてください。
1 2 3 | npm install vue |
CLIを使う
Vue CLIは公式が用意した開発環境のひな形を作成できるコマンドラインツールです。
これを使用することで簡単にプロジェクトを開始することができます。
まずVue CLIをインストールします。
1 2 3 | npm install -g @vue/cli |
次にVue CLIのバージョンを表示させ、ちゃんとインストールできたか確認します。
1 2 3 | vue --version |
確認出来たらプロジェクトを作成します。
1 2 3 | vue create プロジェクト名 |
Vueのバージョンを2、3どちらにするか選ぶと、プロジェクト名のディレクトリが作成されます。
後はVue CLIが指示する通りにコマンドを実行するとサンプルが表示されます。
1 2 3 4 | cd プロジェクト名 npm run serve |
これで準備完了です。
拡張機能
Vueの開発をするなら、Chromeの拡張機能を入れておきましょう。
表示させる
ではコードを書いてみましょう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app"> <h1>{{ message }}</h1> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { message: 'Hello world!' } }) </script> </body> </html> |
はい、画面にHello world!と表示されています。
こんな手軽にVueを始めることができるのですね。
また検証のコンソールで以下のように記述すると、画面が自動的に書き変わったと思います。
これがデータバインディングのおかげですね。データの値が変更されたので、DOMも自動的に変更が反映されています。
1 2 3 | app.message = 'データを変更しました' |
解説
new Vue()でVueコンストラクタからインスタンスを生成されています。これによってVueが起動します。
その際にオプションオブジェクトを引数として渡します。今回はelオプションとdataオプションを渡しました。
elオプションで、Vueの適用範囲となるDOMを指定します。
その範囲外にVueの記述を書いても動作しません。
だいたいidを指定することが多いです。
クラスで指定しても大丈夫です。そのクラスを持つ要素が複数あった場合、最初の1つがVueの範囲となります。
次にdataオプションです。Vueインスタンス時にdataオブジェクトに存在する値のみ、リアクティブシステムに追加されます。
つまり、後でdataオブジェクトにプロパティを追加し、値の更新をしてもDOMは反応しません。データバインディングを利用したいデータは、インスタンス生成時に初期値など設定するようにしてください。
HTML上でデータを参照したい場合は{{ 参照したいdataオブジェクトのプロパティ名 }}でそのプロパティの値が表示されます。
ちなみに、{{}}はMustaches(マシュタッシュ)構文と言います。この構文内はJavaScriptの式をサポートしています。
算出プロパティ・ウォッチャ
Mustaches構文内で同じ処理を何回も書いたり、どんどん処理が複雑になった場合は算出プロパティを使いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-2"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reverseMessage }}"</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app2 = new Vue({ el: '#app-2', data: { message: 'Hello' }, computed: { reverseMessage: function () { return this.message.split('').reverse().join('') } } }) </script> </body> </html> |
算出プロパティはcomputedオプション内に記述します。
宣言した算出プロパティreverseMessageはこのVueインスタンスのdataオプションのmessageプロパティの値に依存して、それによってキャッシュされます。
messageプロパティの値が変われば自動的にreverseMessageの値も変わります。
dataの値が変わらないときに算出プロパティにアクセスしても、関数を実行することなく以前の結果を返します。
またVueは監視プロパティがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-3">{{ fullName }}</div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app3 = new Vue({ el: '#app-3', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } }) </script> </body> </html> |
監視するdataのプロパティ名をwatchオプションの中に記述します。
dataの値が変更されると、関数の処理を実行します。
ですがこれは冗長です。算出プロパティを使った方がいいケースの方が多いです。
watchオプションは、データが変わるのに応じて非同期やコストの高い処理を行いたいときに利用するといいです。
ディレクティブ
v-*から始める属性をディレクティブといいます。属性値は単一のJavaScriptの式(例外もある)をとります。
ディレクティブはHTML要素に記述します。ディレクティブの属性値が変更すると、DOMが自動的に変化します。
いくつかのディレクティブを紹介したいと思います。
v-bind
HTMLの属性値(href、classなどの値)や、コンポーネントのプロパティの値をJavaScriptで動的にしたい場合に使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-4"> <span v-bind:title="message"> Hover your mouse over me for a few seconds to see my dynamically bound title! </span> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app4 = new Vue({ el: '#app-4', data: { message: 'You loaded this page on' + new Date().toLocaleString() } }) </script> </body> </html> |
カーソルを当てると、messageの内容が表示されます。
Mustaches構文で属性値を書こうとしても動きません。属性を動的にしたい場合は、v-bindを使いましょう。
1 2 3 4 | // 展開されず、文字列としてそのまま出力される <span title="{{ message }}"></span> |
v-bindは以下のように記述します。
1 2 3 4 5 6 7 8 9 10 | // HTML要素 v-bind:属性名="JavaScriptの式" // コンポーネント v-bind:コンポーネントのプロパティ名="JavaScriptの式" // 省略記法 :属性名="JavaScriptの式" |
条件付きレンダリング
v-ifは条件を満たしている場合に要素が描画されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-5"> <span v-if="seen">Now you see me</span> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app5 = new Vue({ el: '#app-5', data: { seen: true } }) </script> </body> </html> |
データseenプロパティの値がtrueなので表示されました。
v-ifは以下のように記述します。
1 2 3 4 5 | <span v-if="条件式">true</span> <span v-else-if="条件式2"></span> <span v-else>false</span> |
また複数の要素を条件によって出し分けしたいが、対象を覆うHTML要素がないことがあります。
そんな時はtemplateタグを使用することで、templateタグはHTML要素として表示せずに出し分けすることができます。
1 2 3 4 5 6 7 | <template v-if="seen"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> |
条件付きレンダリングのディレクティブとしてv-showというのもあります。使い方はv-ifとほぼ同じです。
v-ifは条件を満たさない場合はDOM要素自体を表示させないのに対して、v-showは条件を満たさない場合もDOM要素は存在しdisplayプロパティで出し分けしています。
・v-ifのとき
・v-showのとき
他にもv-showはv-ifと比べて、以下の点が異なります。
・v-elseが使えない
・templateタグを使えない
v-ifとv-showどちらを使うか迷った場合は
・v-ifはDOMを作成・削除と作り直している
・v-showがスタイルによって切り替えている
上記の点を踏まえて、頻繁に表示・非表示が切り替わる場合はv-show、最初の1回ぐらいでほとんど切り替わらない場合はv-ifを採用するのがいいです。
リストレンダリング
v-forを使用すると、配列やオブジェクトの値を繰り返し表示できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-6"> <ol> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ol> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app6 = new Vue({ el: '#app-6', data: { todos: [ { id: 1, text: 'Learn JavaScript' }, { id: 2, text: 'Learn Vue' }, { id: 3, text: 'Build something awesome' } ] } }) </script> </body> </html> |
繰り返したい要素に以下のように指定します。
1 2 3 4 5 | <ul> <li v-for="値 in 配列、オブジェクト名"></li> </ul> |
複数の要素を繰り返したいが、対象を覆うHTML要素がない場合があったとします。
そんな時はv-ifのときと同様にtemplateタグが利用できます。
1 2 3 4 5 6 7 | <template v-for="todo in todos" :key="todo.id"> <p>{{ todo.text }}</p> <button>完了</button> <button>削除</button> </template> |
メソッド・イベントハンドリング
v-onを使うことで、DOMイベントの購読・イベント発火時のJavaScriptの実行することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-7"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app7 = new Vue({ el: '#app-7', data: { counter: 0 } }) </script> </body> </html> |
これはボタンをクリックするとデータcounterプロパティの値が+1されます。
counterの値が変わると、それに伴いDOMが更新されます。
上のコードはv-onディレクティブに直接JavaScriptの記述を書いています。処理が長くなるとコードが読みづらいので、メソッドを指定して呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-7"> <button v-on:click="onClickCounter">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app7 = new Vue({ el: '#app-7', data: { counter: 0 }, methods: { onClickCounter: function () { this.counter += 1 } } }) </script> </body> </html> |
メソッドはmethodsオプション内で定義します。
定義した関数でdataオプションなどの値を使用するときは
1 2 3 | <b>this.dataオプションのプロパティ名</b> |
のように書きます。thisを忘れないように気を付けてください。
また関数を定義するときはアロー関数を使用しないでください。
thisはVueインスタンスであることを求められていますが、アロー関数内のthisは束縛されないため想定と違った結果となります。
v-onは以下のように記述します。
1 2 3 4 5 6 7 | // HTML要素 v-on:イベント名="JavaScriptの式またはメソッド名" // 省略記法 @イベント名="JavaScriptの式またはメソッド名" |
フォーム入力バインディング
input、textarea、select要素にv-modelを指定することで双方向データバインディングを行うことができます。
今までデータの値が変更されるとDOMに反映されました。DOMの上で変更しても、dataプロパティの値に影響はありません。
しかしv-modelを設定したDOMの値を変更するとdataプロパティの値も自動的に変更されます。逆も同様です。
これが双方向データバインディングといいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門</title> </head> <body> <div id="app-8"> <p>{{ message }}</p> <input v-model="message"> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app8 = new Vue({ el: '#app-8', data: { message: 'Hello Vue!' } }) </script> </body> </html> |
v-modelは基本的な糖衣構文 (syntax sugar)で、以下のコードと同じ動作をします。
1 2 3 4 5 6 | <input v-bind:value="message" v-on:input="message = $event.target.value" > |
まとめ
Vue.jsを導入するには、もっと環境設定などしないといけない印象でした。
ですがjQueryみたいにjsファイルを読み込むだけで始められるのがいいですね。またReactと違ってJSXを使わずHTML内に記述するので、デザイナーの方にも受け入れられやすいのかなと思いました。
まだまだWeb制作ではjQueryが現役ですが、ページ内で状態が頻繁に変化するアプリケーションのようなサイトではVue.jsの導入を検討してもいいかもしれません。
Vue.jsは公式ドキュメントが充実していますので、慣れたらこっちを参照するのがベストです。
今回はVue.jsの構文に絞って説明しました。コンポーネントやライフサイクルなども紹介したいですし、実際に何か作っているのも書きたいですね。
では。
参考サイト
ToDoリストを作りながら学習しよう!
Vue.js入門 (1) el, data
Vue.js入門 ―最速で作るシンプルなWebアプリケーション