N予備校1章終えた人向けVue入門3
こちらの記事の続きです。
Step7 リストレンダリング
次は、Vueびにおけるリストの扱い方を学びます。従来であればリストといえば、次のような書き方でした。
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
Vueではこのような繰り返し要素の書き方が、forループのように書けます。最多のサンプルコードを見てください。
<template>
<ul>
<li v-for="item in items" :key="item">
{{ item }}
</li>
</ul>
</template>
<script>
export default {
data(){
return {
items: ['item1', 'item2', 'item3']
}
}
}
</script>
li
タグに見慣れないv-for
とkey
がありますね。v-for="item in items"
とは、data()
で定義したitems
配列をひとつずつitem
に代入して、繰り返しそのタグを生成するという意味です。item
は<li>
タグから閉じタグ</li>
までの範囲で、利用できます。
key
はv-for
を使う場合はセットでつけなければいけません。Vueはどのタグの内容が変更されてどのタグの内容を実際にブラウザで描画しないといけないのか常に考えています。この「どの」に当たる部分が、v-for
のリスト生成だとVueは識別できないみたいなので、こちら側が識別用のkey
というものを指定して上げる必要があります。key
には配列の要素固有の値を入れてあげてください。文字でも数字でも大丈夫です。
ではチュートリアルをみてみましょう。
<script>
// give each todo a unique id
let id = 0
export default {
data() {
return {
newTodo: '',
todos: [
{ id: id++, text: 'Learn HTML' },
{ id: id++, text: 'Learn JavaScript' },
{ id: id++, text: 'Learn Vue' }
]
}
},
methods: {
addTodo() {
// ...
this.newTodo = ''
},
removeTodo(todo) {
// ...
}
}
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>
変数newTodo
がdata()
で定義されています。これをv-model
でinput
タグと連携させています。つまりinput
タグになにかテキストが打ち込まれたらnewTodo
に値が代入されるということですね。
またform
タグにsubmit
イベントが定義されています。formタグはformタグの中にbuttonタグをおいておくと、buttonクリック時にsubmitイベントが発火します。
今回はこれを利用して、addTodo
関数を発火させています。
このTODOリストを動くようにするためには、次のことを行います。
-
addTodo
関数が実行されると、変数todos
の配列に、変数newTodo
の内容を追加する。2通りの方法がある。
// 追加
this.todos.push({ id: id++, text: 'aaa' }); // このaaaを`newTodo`の内容にしたい
-
removeTodo
関数が「X」ボタンと紐付いている。この関数が実行されると該当するtodoを削除したい。次の配列の持つfilter
関数を使って、これを実現する。
// 削除
this.todos = this.todos.filter(/* ... */)
配列はfilter
の他にmap
やreduce
といった関数を持ちます。これらはforループの亜種だと考えてください。N予備校1章では、forループを学びましたが、モダンjs開発は基本的にforループは使わずに、map
やfilter
やreduce
を使います。
ではチュートリアルに挑戦してみましょう!
Step7は以上です。
Step8 算出プロパティー
今回は算出プロパティーを学びます。data()
を理解できていれば、難しくはありません。
次のサンプルコードを見てください。
<template>
<div>
<span>挨拶: {{aisatu}}</span><br>
<span>元気な挨拶: {{aisatu}}</span>
</div>
</template>
<script>
export default {
data(){
return {
aisatu: 'こんにちは'
}
}
}
</script>
挨拶: こんにちは
元気な挨拶: こんにちは
が表示されますね。元気な挨拶の場合は「!」をつけたいと思います。このようにdata()
で定義した変数を加工表示したいときに 算出プロパティーcomputed は役に立ちます。script
タグ内で、computed
を定義して、そのなかで関数を定義すると、template
で関数名を{{}}
内部で指定できるようになります。
<template>
<div>
<span>挨拶: {{aisatu}}</span><br>
<span>元気な挨拶: {{ genki }}</span>
</div>
</template>
<script>
export default {
data(){
return {
aisatu: 'こんにちは'
}
},
computed: {
genki() {
return this.aisatu + "!";
}
}
}
</script>
では問題です。次のサンプルコードにコードを追加して、変数value
の2倍の値(twiceValue)が表示されるようにしてください。
<template>
<div>
<span>もとの値: {{ value }}</span><br>
<span>2倍: {{ twiceValue }}</span>
</div>
</template>
<script>
export default {
data(){
return {
value: 1
}
},
computed: {
twiceValue() {
// これを編集
return 1;
}
}
}
</script>
答え
<template>
<div>
<span>もとの値: {{ value }}</span><br>
<span>2倍: {{ twiceValue }}</span>
</div>
</template>
<script>
export default {
data(){
return {
value: 1
}
},
computed: {
twiceValue() {
// これを編集
return this.value * 2;
}
}
}
</script>
ではチュートリアルをみてみましょう!次のサンプルコードを眺めてください。
<script>
let id = 0
export default {
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
computed: {
// ...
},
methods: {
addTodo() {
this.todos.push({ id: id++, text: this.newTodo, done: false })
this.newTodo = ''
},
removeTodo(todo) {
this.todos = this.todos.filter((t) => t !== todo)
}
}
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>
<style>
.done {
text-decoration: line-through;
}
</style>
data()
に新しくhideCompleted
が定義され、template
にチェックボックスと「Show All(Hide completed)」ボタンが追加された以外はStep7とほぼ同じです。もしStep7をまだ理解してなければ、戻って復習しましょう!
今回は、「Show All(Hide completed)」ボタンがクリックするたびに変数hideCompleted
のtrueとfalseが入れ替わっています。
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
この変数hideCompleted
がtrueの場合、チェックボックスにチェックがついたtodoを画面から見えなくしたいと思います。
チェックボックスにチェックがつけるとサンプルコードではどうなるのかみていきます。以下はリスト部分を抜き出したサンプルコードです。
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
input
タグのtypeがcheckboxになっているので、これはチェックボックスを表示してくれます。
そしてv-model
がtodo.done
に紐付いています。このtodo
はv-for="todo in todos"
で定義されており、配列todos
の1つ1つのtodo
をループで取り出して代入したものです。
data()
を見てみます。todos
にStep7ではなかったdone
が追加されています。
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
どうやら、このdone
にv-model
が紐付いているみたいです。チェックボックスにチェックをいれると、todoのdone
がtrueになり、チェックを外すとfalseになるみたいですね。
話を戻すと、この変数hideCompleted
がtrueの場合、チェックボックスにチェックがついたtodoを画面から見えなくしたいです。これは、変数hideCompleted
がtrueの場合、done
がfalseになっているtodoのみを表示させればいいことになります。
これをどう実現するかというと、そもそもの配列todos
をループさせずに、「todosからdoneがfalseなtodoのみを抜き出した新たな配列(filteredTodos)」をループさせればいいのです。以下はサンプルコードのイメージ。
<li v-for="todo in (doneがfalseなtodosの配列)" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
この「todosからdoneがfalseなtodoのみを抜き出した新たな配列(filteredTodos)」を実現するのにうってつけなのが、算出プロパティー(computed)です。
ということで、以下のサンプルコードを完成させれば、このチュートリアルは完了です。
<script>
let id = 0
export default {
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
computed: {
filteredTodos(){
// 以下のコードを書き換えて、doneがfalseなtodoのみの配列を返しましょう
return this.todos;
}
},
~省略~
}
</script>
<template>
~省略~
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
~省略~
</template>
答え
<script>
let id = 0
export default {
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
computed: {
filteredTodos(){
// 以下のコードを書き換えて、doneがfalseなtodoのみの配列を返しましょう
return this.todos.filter(todo => todo.done === false);
}
},
~省略~
}
</script>
<template>
~省略~
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
~省略~
</template>
Step8は以上です。
Step9 ライフサイクルとテンプレート参照
このStep9まででコンポーネント単体における基本的なよく使う機能は網羅できています。Step10は応用的なもの、Step11以降はコンポーネントの組み合わせの話です。
今回は2つの話がでてきます。コンポーネントのライフサイクルと、テンプレート参照(ref)です。まずはライフサイクルの話をします。
ライフサイクルとは生まれてから、死ぬまでのことを指します。
一般的、コンポーネントが生まれた瞬間(これをマウントといいます)と死ぬ瞬間(これをアンマウントといいます)にのみ処理をしたいときがあります。最初だけ〇〇したいときは、マウント時に処理をするコードを書きます。Vueファイルのどこに書けばよいかというと、script
タグ内で、mounted()
という関数を定義すればよいです。その中のコードは、コンポーネントがマウントされた瞬間に実行されるようになります。
<script>
export default {
data(){},
methods: {},
computed: {},
mounted() {
// コンポーネントがマウントされました。
}
}
</script>
ライフサイクルを体験してみましょう。次のコードエディタでコンソールを開いた状態で、なにかinputにタイピングしてみましょう。mountedが1回、タイピングするたびにupdateが出力されるはずです。
つぎにref
を学びます。ref
は正直慣れるまで難しいです。とりあえず今回理解しなくても、また必要になったときに学び直せば良いと思います。
まず、なぜref
が必要になるかという話をします。実は今まで黙っていましたが、template
タグ内部のhtmlは本物のhtmlではありません。これがそのままブラウザに描画されるのではなく、Vueがtemplate
タグを含む.vue
ファイルを解釈して、実際のhtmlを生成します。htmlのタグのことをdom(ドム)といったりしますが、template
タグのhtmlを 仮想dom 、実際に生成されるhtmlを 生dom と言ったりします。
さて、この仮想domですが、実際にどれくらいの高さや幅になるかは、描画されるまでわかりません。実際の描画サイズをもとに、なにかしたい場合は、生dom にアクセスする必要があります。従来であれば、idをつけてdocument.getElementById
でdomを取得していましたが、Vueには専用の方法が提供されています。それがref
です(多分、reference(参照)の略)。
以下は、テキスト全体の幅と高さを取得して表示するサンプルコードです。
vueを使うからには、getElementById
をなるべく使わないようにしましょう。フレームワークが提供する思想にそって使っていくことが、そのフレームワークを使いこなすコツです。
ではチュートリアルをやってみましょう。「hello」の文字列をid
属性をつけずに、適当に書き換えてください。mounted()
を使用します。
<script>
export default {
}
</script>
<template>
<p ref="p">hello</p>
</template>
以上Step9でした。お疲れさまです。
Discussion