はい。百式先生のドットインストールのVue講座を進めています。
とりあえずモリモリ進めてみたんですが、内容を復習も兼ねてまとめたくなったんで書いときます。
Vue.js とは
JavaScript謹製のUI 部品を作るためのフレームワーク。使えると超便利。中国系アメリカ人の方が作ってるらしく中国語でもサポート充実で中国でも人気らしいです。
また、AngularJS等と同じく、双方向データバインディングができるのが大きな特徴。
双方向データバインディングとは、データが更新されればUIが更新され、UIが更新されればデータが更新される仕組みのことです。
基本的には、jQueryなんかと同じくライブラリの本体を読み込むとブラウザあれば動くようになります。
ミニマルなVue.js
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
(function() {
'use strict';
})();
用語解説
ざっくり用語解説です。特にわからなければリンク先をじっくり読んだりしてしっかり感じ取ってください。
View Model
ViewModel とは Vue.js のインスタンスのこと。
View と Model をとりもつオブジェクト。
Vue.jsではじめるMVVM入門
インスタンス
インスタンス (instance)とはオブジェクト指向で出てくる概念のひとつで設計図(クラス)を具現化した「実体」のこと
インスタンス (instance)
インスタンスとオブジェクトの違い
ディレクティブ
ディレクティブとは、DOM 要素に対して何かを実行することをライブラリに伝達する、マークアップ中の特別なトークンです。
ディレクティブ
個人的には、このディレクティブが Vue.js が Vue.js である最大の特徴であり、Vue.js そのものであると言っても過言ではないと思いますたぶん。
Vue.jsのディレクティブ ≒ 他プログラミング言語の制御構造
みたいな認識なのですがあんまり自信はないですはい。
v-model ディレクティブ
v-model はUIとViewModelオブジェクトを紐付けるディレクティブです。
index.html
まずはVue.jsで扱うUIの領域を id = app として div で定義します。main.jsのViewModelインスタンスから受け取ったデータは {{ }} の中に書くことで表現できます。
次に、UIから data へ反映させるには input タグを使用して v-model=”name” というディレクティブを使用することで main.js 内の ViewModel インスタンスの name データと紐付けすることができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
<p>Hello {{ name }}!</p>
<p><input type="text" v-model="name"></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
UI ( id=app ) に結びつくデータを作るため、 ViewModel インスタンス ( var vm )を作成し、どの領域の UI と結びつけるのかを el (elementsの略) キーで指定し、data キーで保持するデータを指定します。
(function() {
'use strict';
//
var vm = new Vue({
el: '#app',
data: {
name: 'taguchi'
}
});
})();
v-for ディレクティブ
v-for は配列をループさせて表示するためのディレクティブです。
List Rendering
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
<h1>My Todos</h1>
<ul>
<li v-for="todo in todos">{{ todo }}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
data には配列形式でデータを保持することもできる。
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
todos: [
'task 1',
'task 2',
'task 3'
]
}
});
})();
v-on ディレクティブ
v-on
ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能になります。
イベントハンドリング
index.html
まずは、input タグに v-model=”newItem” ディレクティブを使用してViewModel インスタンスの newItem データと紐付けします。
次に、フォームが submit された時にViewModel内のメソッドを発火するためにディレクティブとして v-on:submit=”addItem” を使用します。また、 v-on はよく使うので @submit=”addItem” とすることも可能です。
さらに、@submit.prevent=”addItem” とすることでsubmitを押下したときの画面遷移(リロード)を防ぐことができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app" class="container">
<h1>My Todos</h1>
<ul>
<li v-for="todo in todos">{{ todo }}</li>
</ul>
<!-- <form v-on:submit="addItem"> -->
<form @submit.prevent="addItem">
<input type="text" v-model="newItem">
<input type="submit" value="Add">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
UIから受け取る newItem を data に追加します。
次に、v-on ディレクティブで使用するメソッドとして methods というキーを追加して、その中に addItem メソッドを追加します。data 内のデータには this でアクセスできるので this.newItem と書くことができます。
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
newItem: '',
todos: [
'task 1',
'task 2',
'task 3'
]
},
methods: {
addItem: function() {
this.todos.push(this.newItem);
this.newItem = '';
}
}
});
})();
v-onのクリックイベント
index.html
spanタグに @click=”deleteItem(index)”と指定することで main.jsのメソッドを発火させることができます。
v-for は v-for=”(todo, index) in todos” とすることで配列のインデックスを、展開後に使用することができます。
リストレンダリング
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app" class="container">
<h1>My Todos</h1>
<ul>
<li v-for="(todo, index) in todos">
{{ todo }}
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
</ul>
<form @submit.prevent="addItem">
<input type="text" v-model="newItem">
<input type="submit" value="Add">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
ViewModel の methods に deleteItem を追加して、todos の 指定のidの 要素を削除する処理を追加します。
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
newItem: '',
todos: [
'task 1',
'task 2',
'task 3'
]
},
methods: {
addItem: function() {
this.todos.push(this.newItem);
this.newItem = '';
},
deleteItem: function(index) {
if (confirm('are you sure?')) {
this.todos.splice(index, 1);
}
}
}
});
})();
v-bind ディレクティブ
index.html
チェックボックスに対しては v-model=”isDone” を書くことで、isDone が true のときは checked の役割をします。
フォーム入力バインディング
データに応じて css の class を付け替えるには v-bind ディレクティブを使用します。
span タグに v-bind:class=”{done: todo.isDone}” とすると、 VeiewModel データの todo.isDone が true の時は、 css の done クラス が表示されます。
また、v-bind は省略が可能で、 :class=”{done: todo.isDone}” とできます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app" class="container">
<h1>My Todos</h1>
<ul>
<li v-for="(todo, index) in todos">
<input type="checkbox" v-model="todo.isDone">
<!-- <span v-bind:class="{done: todo.isDone}">{{ todo.title }}</span> -->
<span :class="{done: todo.isDone}">{{ todo.title }}</span>
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
</ul>
<form @submit.prevent="addItem">
<input type="text" v-model="newItem">
<input type="submit" value="Add">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
newItem: '',
todos: [{
title: 'task 1',
isDone: false
}, {
title: 'task 2',
isDone: false
}, {
title: 'task 3',
isDone: true
}]
},
methods: {
addItem: function() {
var item = {
title: this.newItem,
isDone: false
};
this.todos.push(item);
this.newItem = '';
},
deleteItem: function(index) {
if (confirm('are you sure?')) {
this.todos.splice(index, 1);
}
}
}
});
})();
v-show ディレクティブ v-if ディレクティブ
index.html
条件的な要素を表示するのは v-show ディレクティブを使用します。また、他の言語のif文と同じような v-if ディレクティブ も使用できます。
条件付きレンダリング
また、一般的に、v-if はより高い切り替えコストを持っているのに対して、 v-show はより高い初期描画コストを持っています。 そのため、とても頻繁に何かを切り替える必要があれば v-show を選び、条件が実行時に変更することがほとんどない場合は、v-if を選びます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app" class="container">
<h1>My Todos</h1>
<!-- <ul>
<li v-for="(todo, index) in todos">
<input type="checkbox" v-model="todo.isDone">
<span :class="{done: todo.isDone}">{{ todo.title }}</span>
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
<li v-show="!todos.length">Nothing to do, yay!</li>
</ul> -->
<!-- <ul>
<li v-if="todos.length" v-for="(todo, index) in todos">
<input type="checkbox" v-model="todo.isDone">
<span :class="{done: todo.isDone}">{{ todo.title }}</span>
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
<li v-else>Nothing to do, yay!</li>
</ul> -->
<ul v-if="todos.length">
<li v-for="(todo, index) in todos">
<input type="checkbox" v-model="todo.isDone">
<span :class="{done: todo.isDone}">{{ todo.title }}</span>
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
</ul>
<ul v-else>
<li>Nothing to do, yay!</li>
</ul>
<form @submit.prevent="addItem">
<input type="text" v-model="newItem">
<input type="submit" value="Add">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
newItem: '',
// todos: [{
// title: 'task 1',
// isDone: false
// }, {
// title: 'task 2',
// isDone: false
// }, {
// title: 'task 3',
// isDone: true
// }]
todos: []
},
methods: {
addItem: function() {
var item = {
title: this.newItem,
isDone: false
};
this.todos.push(item);
this.newItem = '';
},
deleteItem: function(index) {
if (confirm('are you sure?')) {
this.todos.splice(index, 1);
}
}
}
});
})();
computed プロパティ
index.html
spanタグ囲って、 {{ remaining }} を追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app" class="container">
<h1>
My Todos
<span class="info">({{ remaining }}/{{ todos.length }})</span>
</h1>
<ul>
<li v-for="(todo, index) in todos">
<input type="checkbox" v-model="todo.isDone">
<span :class="{done: todo.isDone}">{{ todo.title }}</span>
<span @click="deleteItem(index)" class="command">[x]</span>
</li>
<li v-show="!todos.length">Nothing to do, yay!</li>
</ul>
<form @submit.prevent="addItem">
<input type="text" v-model="newItem">
<input type="submit" value="Add">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
ViewModel内の computed キーはVue インスタンスに組み込まれる算出プロパティ (Computed property) です。
算出プロパティはキャッシュされ、そしてリアクティブ依存が変更されたときにだけ再算出します。ある依存関係がインスタンスのスコープ外の(つまりリアクティブではない)場合、算出プロパティは更新されないことに注意してください。
https://jp.vuejs.org/v2/api/#computed
ViewModel に computed プロパティを追加します。todos.isDone の数が変更されると動的に remaing の値が変更されます。
(function() {
'use strict';
var vm = new Vue({
el: '#app',
data: {
newItem: '',
todos: [{
title: 'task 1',
isDone: false
}, {
title: 'task 2',
isDone: false
}, {
title: 'task 3',
isDone: true
}]
},
methods: {
addItem: function() {
var item = {
title: this.newItem,
isDone: false
};
this.todos.push(item);
this.newItem = '';
},
deleteItem: function(index) {
if (confirm('are you sure?')) {
this.todos.splice(index, 1);
}
}
},
computed: {
remaining: function() {
var items = this.todos.filter(function(todo) {
return !todo.isDone;
});
return items.length;
}
}
});
})();
Component
index.html
コンポーネントは Vue.js の最も強力な機能の 1 つです。基本的な HTML 要素を拡張して再利用可能なコードのカプセル化を助けます。高いレベルでは、コンポーネントは Vue.js のコンパイラが指定された振舞いを加えるカスタム要素です。場合によっては、特別な is
属性で拡張されたネイティブな HTML 要素の姿をとることもあります。
コンポーネント
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
<like-component></like-component>
<like-component></like-component>
<like-component></like-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
(function() {
'use strict';
var likeComponent = Vue.extend({
data: function() {
return {
count: 0
}
},
template: '<button @click="countUp">Like {{ count }}</button>',
methods: {
countUp: function() {
this.count++;
}
}
});
var app = new Vue({
el: '#app',
components: {
'like-component': likeComponent
}
});
})();
Component内のpropsキー
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Vue App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
<like-component message="Like"></like-component>
<like-component message="Awesome"></like-component>
<like-component message="Great"></like-component>
<like-component></like-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
js/main.js
親コンポーネントからデータを受け取るためにエクスポートされた属性のリスト/ハッシュです。シンプルな配列ベースの構文、そして型チェック、カスタム検証そしてデフォルト値などの高度な構成を可能とする配列ベースの代わりとなるオブジェクトベースの構文があります。
https://jp.vuejs.org/v2/api/#props
(function() {
'use strict';
var likeComponent = Vue.extend({
// props: ['message'],
props: {
message: {
type: String,
default: 'Like'
}
},
data: function() {
return {
count: 0
}
},
template: '<button @click="countUp">{{ message }} {{ count }}</button>',
methods: {
countUp: function() {
this.count++;
}
}
});
var app = new Vue({
el: '#app',
components: {
'like-component': likeComponent
}
});
})();