tutorial
Vue.js


Installation — Vue.js


ビルドの種類

UMD
CommonJS
ES Module

Full
vue.js
vue.common.js
vue.esm.js

Runtime-only
vue.runtime.js
vue.runtime.common.js
vue.runtime.esm.js

Full (production)
vue.min.js
-
-

Runtime-only (production)
vue.runtime.min.js
-



  • Full: コンパイラとランタイムの両方が含まれたビルド


  • Compiler: テンプレート文字列を JavaScript レンダリング関数にコンパイルするためのコード


  • Runtime: Vue インスタンスの作成やレンダリング、仮想 DOM の変更などのためのコード
    基本的にコンパイラを除く全てのもの


コンパイルが必要なケース

クライアントでテンプレートをコンパイルする必要がある場合

= template オプションに DOM 内の HTML をテンプレートとして利用し要素にマウントする場合

Full が必要


// これはコンパイラが必要です
new Vue({
template: '<div>{{ hi }}</div>'
})

// これはコンパイラは必要ありません
new Vue({
render (h) {
return h('div', this.hi)
}
})


Introduction — Vue.js


コンポーネントシステムとは

コンポーネントシステム:

「小さく、自己完結的で、(多くの場合)再利用可能なコンポーネント」を組み合わせることで、大規模アプリケーションを構築することが可能になる

アプリケーションのインターフェイスについて考えてみると、ほぼすべてのタイプのインターフェイスはコンポーネントツリーとして抽象化することができる

image.png


サンプル

<html>

<head>
<!-- 開発バージョン、便利なコンソールの警告が含まれています -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<div id="app-2">
<span v-bind:title="message">
Hover your mouse over me for a few seconds
to see my dynamically bound title!
</span>
</div>
<div id="app-3">
<span v-if="seen">Now you see me</span>
</div>
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
<div id="app-7">
<ol>
<!--
各 todo-item の内容を表す todo オブジェクトを与えます。
これにより内容は動的に変化します。
また後述する "key" を各コンポーネントに提供する必要があります。
-->

<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
</ol>
</div>

<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})

var app2 = new Vue({
el: '#app-2',
data: {
message: 'You loaded this page on ' + new Date().toLocaleString()
}
})

var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
})
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
// this.message = this.message.reverse()
}
}
})
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever else humans are supposed to eat' }
]
}
})
</script>
</body>
</html>


The Vue Instance — Vue.js


インスタンスに動的に変数追加はできない

data プロパティは Vue インスタンスが生成された時に存在していた変数のみリアクティブ(つまり、変数が更新されると画面が再度レンダリングされる)


Vue API

Vue インスタンスには、あらかじめプロパティとメソッドが定義されている

先頭に $ をつけることでアクセス可能

var data = { a: 1 }

var vm = new Vue({
el: '#example',
data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch はインスタンスメソッドです
vm.$watch('a', function (newValue, oldValue) {
// このコールバックは `vm.a` の値が変わる時に呼ばれます
})


インスタンスライフサイクルフック


  • created:

  • mouted:

  • updated:

  • destroyed:


ライフサイクルダイアグラム

image.png


Template Syntax — Vue.js


HTML の属性

<div v-bind:id="dynamicId"></div>

dynamicId が Vue インスタンスのプロパティ


省略記法


v-bind

<!-- 完全な構文 -->

<a v-bind:href="url"> ... </a>

<!-- 省略記法 -->
<a :href="url"> ... </a>



v-on

<!-- 完全な構文 -->

<a v-on:click="doSomething"> ... </a>

<!-- 省略記法 -->
<a @click="doSomething"> ... </a>



Computed Properties and Watchers — Vue.js


Computed Propertied

テンプレート内に複雑なロジックを書くべきではない


NG

<div id="example">

{{ message.split('').reverse().join('') }}
</div>


Better

<div id="example">

<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

<script>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 算出 getter 関数
reversedMessage: function () {
// `this` は vm インスタンスを指します
return this.message.split('').reverse().join('')
}
}
})
</script>

## Computed Propertied vs メソッド

```vue
<p>Reversed message: "{{ reverseMessage() }}"</p>

<script>
// コンポーネント内
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
</script>


Computed Properties は依存関係に基づいてキャッシュされる

今回の例だと、 message が変わらない限り reversedMessage は関数を再び実行することなく、以前計算された結果を即時返すこととなる

メソッドは、再描画されるたびに常に関数を実行する


監視プロパティ

何が監視プロパティなのかわからなかった

(おそらく watch

監視プロパティよりも Computed Properties を利用した方が良い

下の方が簡潔


html

<div id="demo">{{ fullName }}</div>



js-NogGood

var vm = new Vue({

el: '#demo',
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
}
}
})


Better

var vm = new Vue({

el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})


ウォッチャ

yes/no の質問を投げると答えてくれる API に ajax で通信

質問がわかるたびに通信するように question を監視


サンプルコード


sample

<html>

<head>
<!-- 開発バージョン、便利なコンソールの警告が含まれています -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

<div id="demo">{{ fullName }}</div>

<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>

<!-- ajax ライブラリの豊富なエコシステムや、汎用的なユーティリティ -->
<!-- メソッドがたくさんあるので、Vue のコアはそれらを再発明せずに -->
<!-- 小さく保たれています。この結果として、慣れ親しんでいるものだけを -->
<!-- 使えるような自由さを Vue は持ち合わせています。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script type="text/javascript">

var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 算出 getter 関数
reversedMessage: function () {
// `this` は vm インスタンスを指します
return this.message.split('').reverse().join('')
}
},
methods: {
reverseMessage: function() {
return this.message.split('').reverse().join('')
}
}
})

// var vm = new Vue({
// el: '#demo',
// 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
// }
// }
// })

var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})

var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// この関数は question が変わるごとに実行されます。
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// _.debounce は特にコストの高い処理の実行を制御するための
// lodash の関数です。この場合は、どのくらい頻繁に yesno.wtf/api
// へのアクセスすべきかを制限するために、ユーザーの入力が完全に
// 終わるのを待ってから ajax リクエストを実行しています。
// _.debounce (とその親戚である _.throttle ) についての詳細は
// https://lodash.com/docs#debounce を見てください。
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>

</script>
</body>
</html>



Class and Style Bindings — Vue.js


HTML のクラス属性のバインド