Vue.js
基本
セットアップ
<div id="app" />
var app = new Vue({
el: '#app',
});
Interpolation (展開)
<span>{{message}}</span>
// index.js
var app = new Vue({
data: {
message: 'hello!',
},
});
bind
属性にデータをバインドする。バインドしないと、ただのテキストとして評価される。
<a v-bind:href="someUrl" /> <a :href="someUrl" />
new Vue({
data: {
someUrl: 'http://someghing.com/',
},
});
if
<span v-if="rock">you rock!</span>
new Vue({
data: {
rock: true,
},
});
for
<li v-for="todo in todos">{{ todo.text }}</li>
new Vue({
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something' },
],
},
});
on
イベントリスナを設定する。
{{message}}
<button v-on:click="onButtonClick" />
<button @click="onButtonClick" />
new Vue({
data: {
message: 'Hello',
},
methods: {
onButtonClick() {
this.message = 'Hello Again!';
},
},
});
model
two-way binding を設定する。
<input v-model="name" />
new Vue({
data: {
name: 'john',
},
});
components
<todo-item v-for="item in groceryList" v-bind:todo="item" />
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>',
});
new Vue({
data: {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever' },
],
},
});
Vue Instance
インスタンスの作成
const vm = new Vue(options); // root Vue instance
Vue.component('some-component', options); // other Vue instances
data
- data プロパティに渡したオブジェクトは、Vue の Reactivity System に組み込まれる。
- インスタンスからの変更、元データの変更は、双方向に反映される。
- data の変更は自動的に View に反映される。
- data に後からプロパティを追加することはできない。
var data = { a: 1 };
var vm = new Vue({
data: data,
});
vm.a === data.a; // => true
vm.a = 2;
data.a; // => 2
data.a = 3;
vm.a; // => 3
規定のプロパティ・メソッド
インスタンスには、名前が$
で始まる、規定のプロパティとメソッドがある。
vm.$data === data; // => true
vm.$el === document.getElementById('example'); // => true
vm.$watch('a', function (newValue, oldValue) {
// This callback will be called when `vm.a` changes
});
Lifecycle Hooks
- いくつかのライフサイクルメソッドがある
- ライフサイクルメソッドの中の
this
は、常に Vue インスタンスを指す(アロー関数は使えないので注意する)
![lifecycle](https://vuejs.org/images/lifecycle.png =500x)
Template Syntax
- テンプレートは Valid な HTML である。
- テンプレートを使わずに、JSX と
render
ファンクションを使うこ ともできる。
Interpolation(Vue のデータをテンプレートに展開する)
Text
- Mustache 記法を使う
<span>Message: {{ msg }}</span>
<span v-once>This will never change: {{ msg }}</span> // 最初の1回のみ更新
<!-- 下記は機能しない。代わりにv-modelを使うこと。 -->
<textarea>{{text}}</textarea>
Raw HTML
- v-html ディレクティブを使う
<span v-html="rawHtml" />
Attributes
- 属性の中では Mustache 記法は使えない。代わりに v-bind を使う。
- null, undefined, false の場合、属性はレンダリングされない
<div v-bind:id="dynamicId"></div>
<div v-bind:active="isActive"></div>
// falsyならactiveはレンダリングされない
Javascript
テンプレートには 1 文までの Javascript を記載できる。
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('')
}}
<div v-bind:id="'list-' + id"></div>
Directives
v-**
がディレクティブである- ディレクティブには 1 文までの Javascript を記載できる
modifier
v-on
とv-model
には Modifier という便利なものが用意されている(後述)
Computed Properties and Watchers
computed
- 複雑な計算には
computed
プロパティを使う。 comupted
は Getter として機能する。computed
が依存するdata
がアップデートされたときは自動で再計算され、View に反映される
<p>{{ reversedMessage }}</p>
var vm = new Vue({
data: {
message: 'Hello',
},
computed: {
reversedMessage: function () {
return this.message.split('');
},
},
});
computed と method の違い
computed
と同じことはmethods
でもできる。違いは下記の通り。
computed
は、依存するdata
が変更されない限り再計算をしない(キャッシュが使われる)methods
は、常に再計算をする
computed と watch の違い
殆どの場合watch
でデータを変更するのは効率が悪い。
computed
を使え。
watch
が最適となるのは、データの変更に応じて、非同期 or 高価な処理を行う場合に限る(参考)
setter
computed
は、標準では getter としてのみ機能する。
setter を使いたいときは下記のようにする。
computed: {
fullName: {
get: function () {},
set: function (newValue) {}
}
}
Class and Style Bindings
Classes
v-bind:class
を使うことで、クラスを動的に設定できる- オブジェクトはインラインでなくても OK(外出ししてもよい)
computed
で計算したオブジェクトを使うと、便利で強力である- Array Syntax で複数の要素を指定することもできる
<div
class="some-default-class"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
<!-- errorClass='some-string' -->
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
Component に Class を指定したとき
Component に Class を指定したときは、そのコンポーネントのルート要素にそのクラスが設定される。
<my-component class="baz boo"></my-component>
Styles
- Class の場合とほぼ同じ
- Auto Prefix される
<div v-bind:style="styleObject"></div>
<div v-bind:style="[baseStyles, overridingStyles]"></div>
Conditional Rendering
v-if
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else-if="type === 'C'">C</div>
<div v-else>Not A/B/C</div>
template
2 つ以上の要素にまとめて v-if を設定したいときは template を使う。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
DOM の再利用
- v-if は、DOM を再利用する。再利用させたくない場合は
key
属性を指定する。 - 下記の場合、label は再利用されるが、input は再利用されない。
<template v-if="loginType === 'username'">
<label>Username</label>
<input key="username-input" />
</template>
<template v-else>
<label>Email</label>
<input key="email-input" />
</template>
v-show
v-if との違いは下記の通り
v-if
- 条件が True になったとき、初めて内包する要素が生成される。
- 条件が False になったときは要素が削除される。
- トグルのコストが高い
- あまりトグルしないものに向いている
v-show
- template, v-else は使えない
- 条件に関係なく常に生成される。css の display を変更しているだけ。
- 初期表示のコストが高い
- 頻繁にトグルするものに向いている
v-if と v-for
v-if
とv-for
を同時に使った場合、v-for
が優先される。- つまり、
v-for
の各子要素に対してv-if
がアタッチされる
List Rendering
v-for
- Array や Object にループ処理を行った上でレンダリングするためのもの。
in
はof
に置き換えてもよい。
<!-- Array -->
<li v-for="item in array"></li>
<li v-for="(item, index) in array"></li>
<!-- Object -->
<li v-for="item in object"></li>
<div v-for="(value, key) in object">
<div v-for="(value, key, index) in object"></div>
</div>
key
リスト要素には、必ず key 要素をつけること。 (デフォルトの"in-place patch"という方法を意図的に使いたい場合を除く)
Array の変更検知
data の Array に対して行った、push()
,pop()
,shift()
,unshift()
,splice()
,sort()
,reverse()
などの変更は、自動的に View に反映される。
filter()
やconcat()
等の元データを変更しないメソッドの場合は、元データを書き換えるのを忘れないこと。
また、下記の操作は Vue が検知できないので注意。
// インデックスを使用した値の変更
vm.items[indexOfItem] = newValue;
// Arrayのlengthの編集
vm.items.length = newLength;
そんなときはVue.set
やsplice
を使うこと
Vue.set(vm.items, indexOfItem, newValue);
vm.items.splice(indexOfItem, 1, newValue);
Object の変更検知
ルートレベルのプロパティの追加は Vue が検知できない。
var vm = new Vue({
data: {
a: 1,
userProfile: {},
},
});
vm.b = 2; // `vm.b` is NOT reactive
ルートレベルでなければ、set を使うことで追加は可能
Vue.set(vm.userProfile, 'age', 27);
// setではなくObject.Assignなどを使いたいときは、
// 元のオブジェクトは捨てて、フレッシュなオブジェクトをセットすること
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green',
});
配列にフィルタ・ソートをかけるには
フィルタ・ソートをかけたいときは、computed
ormethod
を使うと便利。
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
指定回数だけ v-for を実行
<span v-for="n in 10">{{ n }} </span>
<!-- 1,2,3,,,,,10 -->
複数の要素を繰り返す
v-if と同じく、template を使う。
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
v-for をコンポーネントで使う
通常の要素と同じように、コンポーネントにも v-for が使える。 ただし、コンポーネント内からは外部の値にアクセス出来ないので、必要なものは明示的に props として渡す必要がある。
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
Event Handling
イベントを Listen する
<!-- `counter` is the name of a variable -->
<button v-on:click="counter += 1">Add 1</button>
<!-- `greet` is the name of a method. JSXと異なり、()をつけてもInvokeされない -->
<button v-on:click="greet">Greet</button>
<button v-on:click="greet('hello')">Greet</button>
<!-- イベントを渡したいときは$eventを使う -->
<button v-on:click="greet('hello', $event)"></button>
Event Modifier
- DOM 専用
.stop
propagation を止める(バブリングフェーズでの外側のイベントの発生を止める).prevent
preventDefault().capture
capture モードにする(キャプチャフェーズでイベントを発生させる).self
ターゲットが自分自身であったときのみイベントを発生させる.passive
passive イベントとして設定(preventDefault しないことを宣言。参考資料)
- DOM/Component で使用可
.once
一度だけ実行
注意点
- 適用順に注意
v-on:click.prevent.self
- バブリングを含めた全てのフェーズにおいて prevent する
- ターゲットが自分自身のフェーズにおいてのみ、click イベントが発生する
v-on:click.self.prevent
- ターゲットが自分自身のフェーズにおいてのみ click イベントが発生かつ prevent する
- 当然だが、
prevent
とpassive
は同時に使用できない
Key Modifier
<input v-on:keyup.13="submit" />
<!-- same as above -->
<input v-on:keyup.enter="submit" />