【Vue.js】基礎
Vue.jsの学習記録。
- CodePen
- mount
- データバインディング
- コンポーネント
- ディレクティブ
- v-bindディレクティブ
- v-ifディレクティブ
- v-forディレクティブ
- v-onディレクティブ
- v-modelディレクティブ
- v-onceディレクティブ
- v-preディレクティブ
- v-htmlディレクティブ
- v-cloakディレクティブ
- v-textディレクティブ
- JavaScript式
- 算出プロパティ computed
- 算出プロパティとメソッドの違い
- 監視プロパティ(ウォッチャ)
- 算出プロパティと監視プロパティ
- deepオプション
- クラスのデータバインディング
- v-bindによるクラスのバインディングとプレーンなクラス属性の共存
- 配列構文による複数クラスのバインディング
- データオプションにクラスを定義してv-bindへ渡す
- インラインスタイルのデータバインディング
- インラインスタイルのデータバインディングにオブジェクトデータを使う
- v-ifとv-elseディレクティブ
- v-else-ifディレクティブ
- v-showディレクティブ
- v-ifとv-showディレクティブ
- インラインメソッドハンドラ
- メソッドイベントハンドラ
- イベントオブジェクトの参照
- イベントハンドラに引数を渡す
- $event
- イベント修飾子 .once
- v-onの省略記法
- textarea要素のデータバインディング
- チェックボックス
- ラジオボタン
- セレクトボックス
- コンポーネントの定義 グローバル登録
- コンポーネントの定義 ローカル登録
- コンポーネントの命名ルール
- コンポーネントで動的な処理を行う
- トランジション
CodePen
HTML、CSS、JavaScriptの学習を手軽に始めることができる。
mount
マウントとは、既存のDOM要素をVue.jsが生成するDOM要素で置き換えること。
id="app"を指定した要素内が、vue.jsが構築したDOMに置き換えられる。
html
<div id="app"> </div>
js
const app = Vue.createApp({ // options }) app.mount('#app')
以下の記法でもよい。
js
const app = Vue.createApp({ // options }).mount('#app')
データバインディング
データと描画を同期する仕組み。
{{ }} この記法をマスタッシュ構文と呼ぶ。
html
<div id="app"> <p>{{ message }}</p> <p>{{ count }}</p> <p>{{ user.prefecture }}</p> <p>{{ colors[1] }}</p> </div>
js
const app = Vue.createApp({ data: () => ({ message: 'Hello Vue.js!', count: 99, user: { lastName: 'Yamazaki', firstName: 'Kento', prefecture: 'Tokyo' }, colors: ['Red', 'Green', 'Blue'] }) }) app.maunt('#app')
コンポーネント
- 名前付きの再利用可能なインスタンス
- ページを構成するUI部品
- テンプレートとそのロジックから構成される
以下はHelloコンポーネントを使って、Helloと出力する処理を再利用している。
ディレクティブ
- v-で始まる特別な属性のこと
- directove(指令)という名前の通り、VUe.jsに何らかの支持を行う仕組み
ディレクティブの例
- v-bind
- v-if
- v-show
- v-for
- v-on
- v-model
使用例
<input type="text" v-bind:value="message" />
v-bindディレクティブ
属性へデータバインディングする記法。
マスタッシュ構文ではv-bindを使用する。
html
<div id="app"> <input type="text" v-bind:value="message"> </div>
js
const app = Vue.createApp({ data: () => ({ message: 'Hello Vue.js' }) }) app.mount('#app')
省略式
どちらを使っても良いが、プロジェクト内で統一されていれば良い。
慣れるまでは省略しない記法で書けば良い。
<a v-bind:href="url">Link</a> ↓ <a :href="url">Link</a>
v-ifディレクティブ
- 要素の表示/非表示を切り替えることのできるディレクティブ
toggleがtrueの場合はHelloと表示する例。
html
<div id="app"> <p v-if="toggle">Hello</p> </div>
js
const app = Vue.createApp({ data: () => ({ toggle: true }) }) app.mount('#app')
v-forディレクティブ
配列やオブジェクトを繰り返し処理で描画できる。
配列の例:
オブジェクトの例:
v-onディレクティブ
イベント処理を行うディレクティブ。
ボタンをクリックすると現在時刻を表示する例。
v-modelディレクティブ
双方向データバインディング:とは、
dataオブジェクトの値変更⇒テンプレートの値変更
に加えて、
テンプレートの値変更⇒dataオブジェクトの値変更
ができる。
v-onceディレクティブ
- 初回だけテンプレートを評価し、それ以降は静的なコンテンツとして扱う。
- つまり初回だけテキストバインディングを行いたいときに利用する。
- API呼び出しを画面がロードされたときだけ行いたい時など。
- ページ更新のパフォーマンスの向上が図れる。
v-onceディレクティブを使っていないときは、ボタンをクリックすると文字が毎回反転するが、
v-onceディレクティブを使うと初回以降は静的なコンテンツとして扱われ、文字が反転しなくなる。
v-preディレクティブ
- 要素と全ての子要素のコンパイルをスキップしたいときに利用する。
- 生のMustache(マスタッシュ)タグを表示したいとき。
- ディレクティブの無い大量のノードをスキップして、コンパイルのスピードを上げたいとき。
v-htmlディレクティブ
- プレーンなHTMLを挿入したいときに利用する。
jsに書いたタグをテキストではなくHTMLとして扱う例。
注意事項
- クロスサイトスクリプティングの脆弱性を利用される恐れがあるため、v-htmlディレクティブは慎重に利用する。
- ユーザが入力した情報には使用しない。
v-cloakディレクティブ
覆い隠す、の意味。
v-textディレクティブ
- Mustacheの代わりにディレクティブを使いたい時に利用する。
- どちらを使ってもよく、統一すればよい。
- どちらでもよければMustache構文を利用する。
- v-textディレクティブは配下のテキストを丸ごと置き換えてしまう。テキストの一部を置き換えたい場合はMustache構文を利用する必要があるため。
JavaScript式
データバインディング内部ではJavaScript式を利用できる。
{{ number + 1 }}
算出プロパティ computed
- 関数によって算出したデータを返すことができるプロパティ。
- ロジックを再利用したいときに利用する。
算出プロパティとメソッドの違い
computed | methods | |
---|---|---|
呼び出し方 | ()が不要 | ()が必要 |
getter/setter | getter/setterを定義できる | getterのみ定義できる |
キャッシュ | あり(前回の計算結果を利用する) | なし(呼び出される度に実行される) |
getter/setterの例
キャッシュの例
computedで実行したランダム関数はキャッシュされているが、methodsのランダム関数はキャッシュされず毎回実行されている。
監視プロパティ(ウォッチャ)
- 特定データ、または算出プロパティの状態を監視して、変化があったときに登録した処理を自動的に実行できるもの。
- 検索フォームの値が変わったタイミングで自動的にAjaxを行って結果を一覧表示するなど。
dataオプションのmessageプロパティが変化したときに、監視プロパティのmessageがフックされ、コンソールにログを出力する例。
監視プロパティを使った単位変換アプリの例
算出プロパティと監視プロパティ
- どちらでも実装できる場合は、算出プロパティを利用する。
- シンプルに記述できるため。
deepオプション
- 監視プロパティでネストされたオブジェクトを監視する場合に利用する。
- deepオプションはデフォルトfalse。
クラスのデータバインディング
プロパティの値によって動的にスタイルを変更する例。
複数のクラスを適用できる。
クラス名にハイフンを含む場合はシングルクォートで囲む必要がある。
html
<p>Hello <span v-bind:class="{ large: isLarge, 'text-danger': hasError }">Vue.js!</span></p>
v-bindによるクラスのバインディングとプレーンなクラス属性の共存
以下のようにv-bind:classとclass=を共存して記述できる。
スタイルが競合する際は、より後に記述したスタイルが優先される。
html
<p>Hello <span class="bg-gray text-blue" v-bind:class="{ large: isLarge, 'text-danger': hasError }">Vue.js!</span></p>
配列構文による複数クラスのバインディング
次のように書くことで複数クラスをバインディングすることができる。
html
<p>Hello! <span v-bind:class="[largeClass, dangerClass]">Vue.js!</span></p>
js
const app = Vue.createApp({ data: () => ({ largeClass: 'large', dangerClass: 'text-danger' }) }) app.mount('#app9')
データオプションにクラスを定義してv-bindへ渡す
html
<p>Hello! <span v-bind:class="classObject">Vue.js!</span></p>
js
const app = Vue.createApp({ data: () => ({ classObject: { large: true, 'text-danger': true } }) }) app.mount('#app9')
インラインスタイルのデータバインディング
一般的にcssをhtmlへ直書きすることは推奨されないが、デバッグ時など一時的に利用する場合はある。
インラインスタイルとは以下のような記述のこと。
html
<p style="color: red;">Hello</p>
インラインスタイルを使ったデータバインディングの例。 html
<p>Hello! <span v-bind:style="{ color: color, fontSize: fontSize + `px` }">Vue.js!</span></p>
js
const app = Vue.createApp({ data: () => ({ color: 'blue', fontSize: 40 }) }) app.mount('#app9')
インラインスタイルのデータバインディングにオブジェクトデータを使う
前述の例よりも見通しが良くなる。
html
<p>Hello! <span v-bind:style="styleObject">Vue.js!</span></p>
js
const app = Vue.createApp({ data: () => ({ styleObject: { color: 'blue', fontSize: '40px', } }) }) app.mount('#app9')
v-ifとv-elseディレクティブ
html
<p v-if="toggle">Yes</p> <p v-else>No</p>
js
const app = Vue.createApp({ data: () => ({ toggle: true }) }) app.mount('#app10')
v-else-ifディレクティブ
- v-if の “else if block” として機能する。
colorに応じて表示を切り替える信号のサンプルプログラム。
html
<p v-if="color === 'red'">Stop</p> <p v-else-if="color === 'yellow'">Caution</p> <p v-else-if="color === 'blue'">Go</p> <p v-else=>Not red/yellow/blue</p>
js
const app = Vue.createApp({ data: () => ({ color: 'red' }) }) app.mount('#app10')
v-showディレクティブ
- 要素のdisplay CSSプロパティを切り替えることで表示、非表示を切り替える。
- v-showディレクティブの後にv-elseを記述しても連動しない。
toggleがfalseのとき非表示にするサンプル。
html
<p v-show="toggle">Hello Vue.js!</p>
js
const app = Vue.createApp({ data: () => ({ toggle: false, }) }) app.mount('#app10')
display: none; が追加されているため非表示となる。
v-ifとv-showディレクティブ
- v-if
- 要素をDOMから削除、追加する
- 高い切り替えコストが発生する
- v-else, v-else-ifが使える
- 実行時に条件を切り替えることがほとんどない場合に利用すれば良い
- v-show
- CSS display プロパティを切り替えることで表示、非表示を切り替える
- 高い初期描画コストがかかる
- v-else, v-else-ifが使えない
- 実行時に表示、非表示を多く繰り返す場合に利用すれば良い
インラインメソッドハンドラ
ボタンクリック時にv-on:clickに記述した式が実行されるサンプル。
メソッドイベントハンドラ
- v-on属性の値にJavaScript式を記述する方法には限界がある。
- メソッドイベントハンドラはv-on属性の値にイベントハンドラのメソッド名を書く方法。
ボタンを押すとカウントを増やすサンプル。
html
<p>{{ counter }}</p> <button v-on:click="clickHandler">Click</button>
js
const app = Vue.createApp({ data: () => ({ counter: 0 }), methods: { clickHandler: function() { this.counter++ } } }) app.mount('#app11')
イベントオブジェクトの参照
イベントハンドラに引数を指定すると、イベントオブジェクトを取得することができる。
引数名は自由に付けられるが、慣習として event や e がつけられることが多い。
html
<p>{{ counter }}</p> <button v-on:click="clickHandler" id="btn">Click</button>
js
const app = Vue.createApp({ data: () => ({ counter: 0 }), methods: { clickHandler: function (event) { this.counter++ console.log(event.target) console.log(event.target.tagName) console.log(event.target.innerHTML) console.log(event.target.id) } } }) app.mount('#app11')
イベントハンドラに引数を渡す
- ディレクティブに指定したメソッド名に括弧を付けて値を渡す。
htmlからメソッドに値を渡し、プロパティにセット、そのプロパティを画面へ表示するサンプル。
html
<button v-on:click="clickHandler('Vue.js')">Click</button> <p>{{ message }}</p>
js
const app = Vue.createApp({ data: () => ({ message: '' }), methods: { clickHandler: function (message) { this.message = message } } }) app.mount('#app11')
$event
- 前述のようにメソッドに引数を定義した場合は、eventオブジェクトを参照できなくなる。
- その場合は、$eventを引数に追加することで参照できるようになる。
- $event という記法はVue.jsが定義しているため変更できない。
html
<button v-on:click="clickHandler($event, 'Vue.js')">Click</button> <p>{{ message }}</p>
js
const app = Vue.createApp({ data: () => ({ message: '' }), methods: { clickHandler: function ($event, message) { this.message = message console.log($event) } } }) app.mount('#app11')
イベント修飾子 .once
- .onceを付けるとイベントハンドラを一回だけ実行するようになる。
ボタンを押したとき一回だけ現在時刻を表示する例。 html
<button v-on:click.once="clickHandler3">Now</button> <p>{{ now }}</p>
js
const app = Vue.createApp({ data: () => ({ message: '' }), methods: { clickHandler3: function() { this.now = new Date().toLocaleTimeString() } } }) app.mount('#app11')
v-onの省略記法
- v-onディレクティブはよく利用するため省略記法が用意されている。
- どちらを使ってもよいが、プロジェクト内で統一されていた方が良い。
html
<button v-on:click.once="clickHandler3">Now</button> <button @click.once="clickHandler3">Now</button>
textarea要素のデータバインディング
textarea要素はMustache構文を使ったデータバインディングはできない。
html
<textarea>{{ message }}</textarea>
v-modelディレクティブを使えばデータバインディングできる。
html
<textarea v-model="message"></textarea>
js
const app = Vue.createApp({ data: () => ({ message: 'Hello Vue.js' }) }) app.mount('#app12')
チェックボックス
単体
html
<label for="checkbox">{{ checked }}</label> <input type="checkbox" id="checkbox" v-model="checked">
js
const app = Vue.createApp({ data: () => ({ checked: 'false', }) }) app.mount('#app12')
複数
html
<label for="red">Red</label> <input type="checkbox" id="red" value="Red" v-model="colors"> <label for="green">Green</label> <input type="checkbox" id="green" value="Green" v-model="colors"> <label for="blue">Blue</label> <input type="checkbox" id="blue" value="Blue" v-model="colors"> <p>{{ colors }}</p>
js
const app = Vue.createApp({ data: () => ({ colors: [], }) }) app.mount('#app12')
ラジオボタン
html
<label for="red">Red</label> <input type="radio" id="red" value="Red" v-model="color"> <label for="green">Green</label> <input type="radio" id="green" value="Green" v-model="color"> <label for="blue">Blue</label> <input type="radio" id="blue" value="Blue" v-model="color"> <p>{{ color }}</p>
js
const app = Vue.createApp({ data: () => ({ color: '', }) }) app.mount('#app12')
セレクトボックス
単体
html
<select v-model="selected"> <option disabled>Please select one</option> <option>Red</option> <option>Green</option> <option>Blue</option> </select> <p>{{ selected }}</p>
js
const app = Vue.createApp({ data: () => ({ selected: '', }) }) app.mount('#app12')
複数
html
<select v-model="selectedMultiple" multiple> <option>Red</option> <option>Green</option> <option>Blue</option> </select> <p>{{ selectedMultiple }}</p>
js
const app = Vue.createApp({ data: () => ({ selectedMultiple: '', }) }) app.mount('#app12')
v-model修飾子 .lazy
- .lazy: バインドのタイミングを遅延させる
html
<div id="app13"> <input type="text" v-model.lazy="message"> <p>{{ message }}</p> </div>
js
const app = Vue.createApp({ data: () => ({ message: '' }) }) app.mount('#app13')
入力中はバインドされない。
Tabキーなどを押して入力が確定されるとバインドされる。
v-model修飾子 .trim
- .trim: 入力値から前後の空白を削除して、データに代入する。
- 半角、全角ともに削除される。
html
<div id="app13"> <input type="text" v-model.trim="message"> <p>{{ message }}</p> </div>
js
const app = Vue.createApp({ data: () => ({ message: '' }) }) app.mount('#app13')
v-model修飾子 .number
- .number: 入力値を数値型に型変換してからデータに代入する。
.numberがない場合は文字列型となっているため、10+10は1010となる。 html
<div id="app13"> <input type="number" v-model="age"> <p>{{ age + 10 }}</p> </div>
js
const app = Vue.createApp({ data: () => ({ age: 0 }) }) app.mount('#app13')
文字列の結合になっている。
.numberを付けると、入力値を数値型に型変換してからデータに代入できるようになる。 html
<div id="app13"> <input type="number" v-model.number="age"> <p>{{ age + 10 }}</p> </div>
コンポーネントの定義 グローバル登録
html
<div id="app14"> <hello-component></hello-component> <hello-component></hello-component> <hello-component></hello-component> </div>
js
const app = Vue.createApp({ data: () => ({}), }) app.component('hello-component', { template: '<p>Hello!</p>' }) app.mount('#app14')
コンポーネントの定義 ローカル登録
- 特定のVueインスタンス配下でしか利用できないようにできる。
- スコープ制御のために利用する。
ルートのVueインスタンスを定義する前に、ローカルのコンポーネントを定義する。
Vueインスタンスのコンテンツオプションに登録することで、登録したVueインスタンスでのみ利用できるようになる。
html
<div id="app14"> <hello-global-component></hello-global-component> <hello-global-component></hello-global-component> <hello-global-component></hello-global-component> <hello-local-component></hello-local-component> <hello-local-component></hello-local-component> <hello-local-component></hello-local-component> </div>
js
const helloLocalComponent = { template: '<p>Hello local!</p>' } const app = Vue.createApp({ data: () => ({ }), components: { 'hello-local-component': helloLocalComponent } }) app.component('hello-global-component', { template: '<p>Hello!</p>' }) app.mount('#app14')
コンポーネントの命名ルール
- ハイフンを一つ以上含むケバブケースを利用する必要がある
- OK... local-component
- NG... localComponent
- NG... component
全てのHTML要素は一単語となっている背景があり、これらとの衝突を避けるため。
コンポーネントで動的な処理を行う
ボタンを押した回数をカウントするコンポーネントのサンプル。
コンポーネントにすることで再利用できる。
トランジション
ボタンを押すとフェードイン、フェードアウトしながら表示、非表示が切り替わるサンプル。
html
<div id="app15"> <button v-on:click="show = !show">Change</button> <transition name="fade"> <p v-show="show">Transition</p> </transition> </div>
js
const app = Vue.createApp({ data: () => ({ show: true }) }) app.mount('#app15')
.fade-enter-active, .fade-leave-active { transition: opacity 1s; } .fade-enter-from, .fade-leave-to { opacity: 0; }