LearnCode.academy とは?
LearnCode.academy という YouTube チャンネルがあり,Vue.js や React + Redux などを解説した,無料の学習動画が公開されている.今回受講したのは「Vue Tutorial」で,全9回となっている.合計しても「約1時間程度」なので,スキマ時間を活用して気軽に学ぶこともできる.「Vue.js は気になるけどまだ試したことがない」という人にオススメ!
- Vue Tutorial #1 - Vue JS Tutorial for Beginners #1 setting up an app
- Vue Tutorial #2 - Vue.js filters and computed data
- Vue Tutorial #3 - Vue.js directives and event listeners
- Vue Tutorial #4 - Break up your app into Vue Components
- Vue Tutorial #5 - AJAX data and Lifecycle Methods
- Vue Tutorial #6 - Full REST Example
- Vue Tutorial #7 - Scaling Vue with Single File Components
- Vue Tutorial #8 - Vue Router
- Vue Tutorial #9 - Scaling Vue.js Data with Stores
なお,動画の中で実装されているコードは全て GitHub に公開されているので,動画を見ながら写経しても良いし,最初に Vue.js アプリケーションを動かしてから動画を見ることもできる.
Vue Tutorial #1
まず,雛形となる index.html
に,マウントポイントとなる div
を作成した.
<div id="app"></div>
次に,Vue インスタンスを作成する.
el
... マウントポイントとなる要素のセレクターdata
... 処理するデータオブジェクトtemplate
... テンプレート
Vue.js のテンプレートは Mustache 構文になっているので {{john.name}}
のように書けば,データオブジェクトの値を反映することができる.
const app = new Vue({ el: "#app", data: { bobby: { name: "Bobby Boone", age: 25 }, john: { name: "John Boby", age: 35, } }, template: ` <div> <h2>Name: {{john.name}}</h2> <h2>Age: {{john.age}}</h2> <h2>Name: {{bobby.name}}</h2> <h2>Age: {{bobby.age}}</h2> </div> ` })
Vue Tutorial #2
複雑なロジックはテンプレートに書くのではなく,computed
を使って共通化できる.すると {{bobbyFullName}}
のように書けるようになる.
const app = new Vue({ (中略) computed: { bobbyFullName() { return `${this.bobby.first} ${this.bobby.last}` }, johnFullName() { return `${this.john.first} ${this.john.last}` } }, (中略) })
ただし,これだとオブジェクトのデータごとに書く必要があるため,filters
を使ってさらに共通化できる.すると {{bobby | fullName}}
のように書けるようになる.これは bobby
というデータオブジェクトを fullName
にパイプで渡しているイメージになる.
const app = new Vue({ el: "#app", data: { bobby: { first: "Bobby", last: "Boone", age: 25 }, john: { first: "John", last: "Boone", age: 35, } }, computed: { johnAgeInOneYear() { return this.john.age + 1; } }, filters: { ageInOneYear(age) { return age + 1; }, fullName(value) { return `${value.last}, ${value.first}`; } }, template: ` <div> <h2>Name: {{john | fullName}}</h2> <h2>Age: {{john.age | ageInOneYear}}</h2> <h2>Name: {{bobby | fullName}}</h2> <h2>Age: {{bobby.age | ageInOneYear}}</h2> </div> ` })
Vue Tutorial #3
次は v-for
ディレクティブを使って,配列のデータオブジェクトを表示することができた.
<div> <h2 v-for="friend in friends">{{friend | fullName}}</h2> </div>
さらに v-model
ディレクティブを使うと,自動的に値を書き換えるフォームを作ることができた.これはスゴイ!公式ドキュメントを読んだら,v-model
は v-bind:value
と v-on:input
のシンタックスシュガーと書いてあった.
<div> <h2 v-for="friend in friends"> <h4>{{friend | fullName}}</h4> <input v-model="friend.first"/> <input v-model="friend.last"/> </h2> </div>
最後は v-on:click
でボタンにイベントをバインドした.なお,公式ドキュメントに computed
と methods
の違いはキャッシュで,計算結果をキャッシュできる場合は computed
を使うべきと書いてあった.
const app = new Vue({ (中略) methods: { decrementAge(friend) { friend.age = friend.age - 1; }, incrementAge(friend) { friend.age = friend.age + 1; }, }, template: ` <div> <h2 v-for="friend in friends"> <h4>{{friend | fullName}}</h4> <h5>age: {{friend.age}}</h5> <button v-on:click="decrementAge(friend)">-</button> <button v-on:click="incrementAge(friend)">+</button> <input v-model="friend.first"/> <input v-model="friend.last"/> </h2> </div> ` })
Vue Tutorial #4
Vue コンポーネントを使うと,Vue インスタンスの定義をさらに細分化することができる.ただし,Vue コンポーネントの中のデータは外部から参照できないため,子コンポーネント側で props
オプションを使って,親コンポーネントからデータオブジェクトを受け取る必要がある.Vue.js で画面設計をするときに,Vue コンポーネント設計が重要になるという話がよくわかった.
Vue.component('friend-component', { props: ['friend'], filters: { ageInOneYear(age) { return age + 1; }, fullName(value) { return `${value.last}, ${value.first}`; } }, methods: { decrementAge(friend) { friend.age = friend.age - 1; }, incrementAge(friend) { friend.age = friend.age + 1; }, }, template: ` <div> <h4>{{friend | fullName}}</h4> <h5>age: {{friend.age}}</h5> <button v-on:click="decrementAge(friend)">-</button> <button v-on:click="incrementAge(friend)">+</button> <input v-model="friend.first"/> <input v-model="friend.last"/> </div> ` }); const app = new Vue({ (中略) template: ` <div> <friend-component v-for="item in friends" v-bind:friend="item" /> </div> ` })
Vue Tutorial #4 が完了した段階で,以下のようなアプリケーションが実装できた.
(画面イメージ)
Vue Tutorial #5 & Vue Tutorial #6
Vue インスタンスの el
を削除しても,コンソールで app.$mount("#app")
と実行すれば手動でマウントできることを試した.そして,Vue インスタンスには多くのライフサイクルがあり,例として beforeMount
や mounted
を学んだ.今回は fetch()
を使って API を呼び出し,その結果をデータオブジェクトとして利用する一般的な構成を作成した.
また,データを削除する場合は,添字を指定してデータオブジェクトから削除することで,リアクティブに反映される.関連するディレクティブで言えば,v-on:click
だけではなく v-on:keyup.13
を使って Enter 押下のイベントを取得したり,v-if
と v-else
を使ってテンプレートの分岐なども書けた.
const app = new Vue({ el: "#app", data: { editFriend: null, friends: [], }, methods: { deleteFriend(id, i) { fetch("http://rest.learncode.academy/api/vue-5/friends/" + id, { method: "DELETE" }) .then(() => { this.friends.splice(i, 1); }) }, updateFriend(friend) { fetch("http://rest.learncode.academy/api/vue-5/friends/" + friend.id, { body: JSON.stringify(friend), method: "PUT", headers: { "Content-Type": "application/json", }, }) .then(() => { this.editFriend = null; }) } }, mounted() { fetch("http://rest.learncode.academy/api/vue-5/friends") .then(response => response.json()) .then((data) => { this.friends = data; }) }, template: ` <div> <li v-for="friend, i in friends"> <div v-if="editFriend === friend.id"> <input v-on:keyup.13="updateFriend(friend)" v-model="friend.name" /> <button v-on:click="updateFriend(friend)">save</button> </div> <div v-else> <button v-on:click="editFriend = friend.id">edit</button> <button v-on:click="deleteFriend(friend.id, i)">x</button> {{friend.name}} </div> </li> </div> `, });
Vue Tutorial #7
ここから少し難易度が上がり,vue-cli + webpack で Vue.js の環境構築をしながら「単一ファイルコンポーネント」を学んだ.具体的には Tutorial 4 で学んだ Vue コンポーネントを .vue
という拡張子のファイルに分割していくことになる.このときに,Vue コンポーネントの中にスタイルも含めるという考え方は個人的には新鮮だった.確かにスタイルまで揃ってこそのコンポーネントだからスッと理解できた.ただし,コンポーネントが増えてくるとスタイルを共通化したくなる未来もありそうで,そのあたりは外部ファイルを読み込むことになるらしく,公式ドキュメントにも書かれていた.またコンポーネント間でスタイルが競合する場合は <style scoped>
でスコープを狭めることができる.
以下に App.vue
を載せておく.他のコードは GitHub を参照してもらえればと!
<template> <div id="app"> <img src="./assets/logo.png"> <HelloWorld/> </div> </template> <script> import HelloWorld from './components/HelloWorld' export default { name: 'app', components: { HelloWorld } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
Vue Tutorial #8
次は Vue Router を使って SPA を実装した.SPA のルーティングなので,基本的にはハッシュイベント(URL 末尾の #)をルーティングする.今回書いた router/index.js
は以下のようになる.
import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Account from '@/components/Account' import Contact from '@/components/Contact' import Friends from '@/components/Friends' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: HelloWorld }, { path: '/friends/:id/:age/:weight', name: 'Friends', props: true, component: Friends }, { path: '/contact', name: 'Contact', component: Contact }, { path: '/account', name: 'Account', component: Account } ] })
なお,Vue コンポーネント側でルーティングパラメータは {{$route.params.id}}
のように取得できるけど,以下のように props
で定義してしまう方法もある.
<template> <div> <h1>Friends</h1> {{id}} {{age}} {{weight}} </div> </template> <script> export default { props: [ 'id', 'age', 'weight', ] } </script>
Vue Tutorial #8 が完了した段階で,以下のようなアプリケーションが実装できた.
(画面イメージ)
Vue Tutorial #9
最後は Vue.js で Store パターンを実装した.以下のような stores/FriendStore.js
を用意して,データオブジェクトの操作を Store に閉じ込めている.このあたりは一般的なアーキテクチャなので,特に悩む部分はなかった.今回の Tutorial 対象ではないけど,Flux / vuex の実装は試してみたいと思う.
const FriendStore = { data: { friends: ["bobby", "billy"], }, methods: { addFriend(name) { FriendStore.data.friends.push(name); } } }; export default FriendStore;
あとは Vue コンポーネントから Store を呼び出せば,あとはリアクティブに実装ができる.
<template> <div> <h1>My Friends</h1> <li v-for="friend in FriendStore.friends">{{friend}}</li> <input v-model="newFriend" v-on:keyup.13="addFriend(newFriend)" /> </div> </template> <script> import FriendStore from "../stores/FriendStore" export default { data() { return { newFriend: null, FriendStore: FriendStore.data, }; }, methods: { addFriend(name) { FriendStore.methods.addFriend(name) this.newFriend = null; } } } </script>
Vue Tutorial #9 が完了した段階で,以下のようなアプリケーションが実装できた.
(画面イメージ)
まとめ
- LearnCode.academy の「Vue Tutorial」を受講して,はじめて Vue.js を試してみた
- 「Vue.js は気になるけどまだ試したことがない」という人にオススメ!
- MVVM な設計,コンポーネント思想,便利な Vue ディレクティブなど,Vue.js を採用するメリットを感じることができた
- 公式ドキュメントも,日本語ドキュメントも,非常に詳しく書かれていて素晴らしかった
開発ツールなど
今回はじめて Vue.js を書いたので,まだ深く使えていないけど,VS Code + Vetur でコードを書いて,デバッグのときは Chrome 拡張の vue-devtools を使った.
関連記事
jQuery を活用して SPA を実装するのを学ぶには「サーバーレスシングルページアプリケーション」もオススメ!