Vue 入門

Node.js(npm+Browserifyなど)でパッケージ管理したり、サーバサイドで事前処理(.vueファイル、vue-cli)して配信ファイルを生成したりしてややこしいけれど、基本的にVue(Vue.js)はブラウザ上で動作するBootstrap++みたいなクライアントサイドフレームワークという理解。

参考

この「はじめに」をベースにサンプルを並べていく。

何もしないHTML

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<h2>Hello Vue!</h2>
<p>
This is not Vue!

CDNから開発用のVue.jsを持ってくるだけ。これをベースにいじっていく(SRIは略)。

テストするときはpython3 -m http.server --bind 127.0.0.1とか、php -S 127.0.0.1:8000とか、静的コンテンツを開ければなんでもよし。

テンプレート的用法

適当に使ってみる

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<h2>Hello Vue!</h2>
<p id="body">
{{ body_content }}
<script>
(function() {
var app = new Vue({
el: '#body',
data: {
body_content: 'This is BODY!',
},
});
})();
</script>

Django/Jinjaのように{{ variable }}の形式で埋め込み位置を指定。Vueオブジェクトを作って初期値を与える。

コンテナ要素で使ってみる

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<h2>{{ title }}</h2>
<p>
{{ body }}
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
title: 'Hello Vue!',
body: 'This is BODY!',
},
});
})();
</script>

要素の内部に複数の埋め込みを行う。

属性をテンプレート化

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<h2 v-bind:title="tooltip">{{ title }}</h2>
<p>
{{ body }}
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
tooltip: 'Hover text',
title: 'Hello Vue!',
body: 'This is BODY!',
},
});
})();
</script>

属性(この場合、ホバーテキスト/ツールチップを表すtitle属性)に埋め込みを行う(titleという名前が2つ出てきてややこしくなってしまった)。

スクリプトからの書き換え

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<h2 v-bind:title="tooltip">{{ title }}</h2>
<p>
{{ body }}
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
tooltip: 'Hover text',
title: 'Hello Vue!',
body: 'This is BODY!',
},
});
app.title = 'This is TITLE!';
app.tooltip = 'This is TOOLTIP!';
})();
</script>

埋め込みテキストはスクリプトから変更可能(すぐに反映される)。

イベントハンドリング

クリックイベント

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<h2 v-bind:title="tooltip">{{ title }}</h2>
<p>
{{ body }}
<p>
<button v-on:click="onButtonClicked">Switch</button>
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
tooltip: 'Hover text',
title: 'Hello Vue!',
body: 'This is BODY!',
},
methods: {
onButtonClicked: function() {
this.title = 'This is TITLE!'; // this == app
this.tooltip = 'This is TOOLTIP!';
},
},
});
})();
</script>

クリックイベントが起きたとき埋め込みテキストを書き換える。

Vueの内部状態とinputタグ

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<p>
<input v-bind:value="field1">
<p>
<input v-model="field2">
<p>
<button v-on:click="check">Check</button>
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
field1: 'Field 1',
field2: 'Field 2',
},
methods: {
check: function() {
console.log(app.field1);
console.log(app.field2);
},
}
});
app.check();
})();
</script>

Vueの内部状態と画面上の状態がずれたときの挙動を試す。v-bind:valueでは再描画が走ったときに値がVueの内部状態にリセットされる。これを防ぐ(Vueの内部状態と表示状態を同期する)にはv-modelを使う(双方向バインディング)。

スタイル操作

Visibility

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<p>
<button v-on:click="onButtonClicked">Switch</button>
<p v-if="message_visible">
Hello Vue!
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
message_visible: true,
},
methods: {
onButtonClicked: function() {
app.message_visible = !app.message_visible;
},
}
});
})();
</script>

display: noneの切り替え。条件分岐という扱いらしい。

<!-- Django / Jinja -->
{% if message_visible %}
<p>
Hello Vue!
{% endif %}

これの代わりなのはわかるが、タグ名が先に来ているのが読みにくい..(Rubyか?)。

再利用

配列のマッピング

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<div id="app">
<ol>
<li v-for="item in items">
{{ item.text }}
</ol>
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
items: [
{ text: 'Item 1' },
{ text: 'Item 2' },
{ text: 'Item 3' },
],
},
});
})();
</script>

v-forを付けた要素をリストに基づいて複製して個数分表示する。これもタグ名が先に来ているのが読みにくい..。

<!-- Django / Jinja -->
<ol>
{% for item in items %}
<li>
{{ item.text }}
{% endfor %}
</ol>

これの代わりなのはわかる。

コンポーネント

新しいタグ名を定義する感覚で要素の再利用(コード上での定義)ができる。

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<script>
Vue.component('my-item', {
template: '<li>Item</li>',
});
</script>
<div id="app">
<ol>
<my-item v-for="item in items"></my-item>
</ol>
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
items: [
{ text: 'Item 1' },
{ text: 'Item 2' },
{ text: 'Item 3' },
],
},
});
})();
</script>

閉じタグ省略したら警告出た。textをちゃんと使うには、以下のようにする。

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<script>
Vue.component('my-item', {
props: [ 'my_item', ],
template: '<li>{{ my_item.text }}</li>',
});
</script>
<div id="app">
<ol>
<my-item
v-for="item in items"
v-bind:my_item="item"
></my-item>
</ol>
</div>
<script>
(function() {
var app = new Vue({
el: '#app',
data: {
items: [
{ text: 'Item 1' },
{ text: 'Item 2' },
{ text: 'Item 3' },
],
},
});
})();
</script>

v-bind:text="item.text"みたいなこともできるらしい。

Vue CLIって何

今回は単一のHTMLファイルに全部書いてるが、巨大なプロジェクトではそうもいかない。 コンポーネントの定義なんかは外部のJSを持ってくればよさそうだけれど、これも数が多くなると管理がつらくなってくる。 このへんをなんかいい感じ(コンポーネントごとにファイル分けたり)にして、ついでにURL(パス、ルーティング)もいい感じにできる(JSでルーティング設定)ようにして、最終的にブラウザ側でVueの処理をいい感じにできるように整えた静的サーバコンテンツ(GitHub Pagesとかで動くやつ)を生成するツールを作ってみた、というのがVue CLIっぽい。