Vue.jsの基本的な使い方
- Reactは単方向データバインディングで一方通行なのに対し、Vueは双方向データバインディングで双方向にデータを流す仕組みになっている。
- 双方向データバインディングの場合は
js
の中身が変わったらすぐに反映される - 単方向データバインディングの場合は
render
などの描画の処理が単方向であるため即時反映とはならない
- 双方向データバインディングの場合は
環境構築 ---未完成
-
vue.esn.js
というtemplate機能のないランタイム限定ビルドがあるので、これも必要に応じてnpmでインストールする
基本的な書き方
-
new Vue
でインスタンス生成 -
el
でセレクターを書き込み、スコープを指定する -
data
プロパティはオブジェクトの形で自分が使いたいプロパティを定義する- データオブジェクトの中の値を出し入れするための入れ物のようなもの
-
data
で定義したプロパティはhtml
で使うことができる
ファイルのベースづくり
import Vue from 'vue'
new Vue({
el: '#app1',
data: {
message: 'vueのテンプレートの構文。{{}}で囲って処理がかける'
}
})
htmlの方では{{}}
の中でテンプレートの構文やif文など、またインスタンス化したdataのプロパティなども使える
<div id="app1">
{{ message }}
</div>
v-bindを使った属性のバインド
- 単方向のデータバインディング
- dataの変更に応じて表示されるが、HTML側の入力でdataが変更されることはない
-
v-bind:
とすることでインスタンスで定義したdata
のプロパティが属性として出力される -
v-bind:
は省略可能で、:
のみで書くことができる - class属性を渡すときは連想配列の形で渡す
- その連想配列の値が
true
かfalse
かでプロパティ名が付くどうかが判別される - この例の場合、
active
とtext-danger
がtrue
なのでclass="active text-danger
として入ってくる
※ text-danger
のように-
を使っている場合''
または""
で囲む必要がある
import Vue from 'vue'
new Vue({
el: '#app2',
data: {
message: 'このページをロードしたのは ' + new Date().toLocalString(),
classObject: {
active: true,
'text-danger': true
}
}
})
<div id="app2">
<!-- title属性は補足的な情報を与えるときに使用し、ポインタを重ねると吹き出し(ツールチップ)が表示される -->
<span v-bind:title="message" :class="classObject">
この文字にロードした日付が表示される
</span>
</div>
v-if, v-else-if, v-elseを使った条件分岐で表示非表示
v-ifのみでの条件分岐
import Vue from 'vue';
new Vue({
el: '#app3',
data: {
isShow: true
}
})
-
isShow
の名称は任意 -
v-if
の中でtrue
かfalse
かを判定し、true
であれば、そのタグ自体を表示し、false
であれば、そのタグ自体を非表示にする
※ 非表示の場合DOM
自体がなくなる(DOM
の残るdisplay:none
とは異なる)
<div id="app3">
<span v-if="isShow">v-ifを使ったDOMの表示非表示</span>
</div>
v-if, v-else-if, v-elseを使った条件分岐
new Vue({
el: '#app3',
data: {
isShow: 'a'
}
})
<div id="app3">
<span v-if=" isShow === 'a' ">v-ifを使った条件分岐</span>
<span v-else-if=" isShow === 'b' ">v-ifと同階層に書く</span>
<span v-else>v-ifと同階層に書く</span>
</div>
v-showを使った要素の表示非表示
-
v-show
による要素は常に描画され、v-if
と異なりDOMを維持する - styleだけ非表示になっているだけ
(display: none;)
-
v-show
がtrue
なら表示、false
なら非表示(display:none;
)
new Vue({
el: '#app3',
data: {
isShow: true
}
})
<div id="app3">
<span v-show="isShow">v-showで表示</span>
<span v-show="!isShow">v-showで非表示に</span>
</div>
v-forを使ったループ処理
v-for
の値に変数名を割り当てる(今回はtodo
)
todo in todos
とすることでtodo
にjsファイルで定義したtodos
の一つ一つの値のデータが入る
data
プロパティの値を配列リテラルの形式で渡すことで、配列一つ一つを回せる
import Vue from 'vue';
new Vue({
el: '#app4',
data: {
todos: [
{text: 'v-forで'},
{text: 'htmlを'},
{text: 'ループ生成'}
]
}
})
<div id="app4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
<!-- 出力結果 -->
<ol>
<li>v-forで</li>
<li>htmlを</li>
<li>ループ生成</li>
</ol>
v-onを使ったイベント発火
v-on:
のあとにイベント名を指定し、イベント後の発火したいメソッドを指定
v-on:
は省略可能で、@
のみで書くことができる
- vueでは
methods
プロパティを定義でき、viewの中で使いたい関数を定義する- methodsは関数の置き場所として用いられる
- methodsは関数なので、呼べばそのまま実行される
- methodsで算出された値はキャッシュされない
-
changeMessage
という関数名は任意 -
methods
プロパティ内でthis
を使うとdata
プロパティにアクセスできる
※ this.message
でmessage
にアクセスできる
import Vue from 'vue';
new Vue({
el: '#app5',
data: {
message: 'Hello Vue.js'
},
methods: {
changeMessage: function() {
this.message = this.message + '変更しました'
}
}
})
<div id="app5">
<p>{{ message }}</p>
<button v-on:click="changeMessage">メッセージを変える</button>
</div>
<!-- 出力結果 -->
メッセージを変える
<!-- クリック -->
Hello Vue.js変更しました
v-onでtoggle
v-on
では、必ずしもmethodを使わなければいけないというわけではない
以下はtoggle処理の例である
<div id="app5">
<button v-on:click="show = !show">
Toggle
</button>
</div>
v-model 双方向データバインディング
- 双方向のデータバインディング
- dataの変更に応じて表示され、HTMLでフォームの入力を行った際にもdataの値の変更が可能
-
input
要素やtextarea
要素、select
要素に双方向 (two-way) データバインディングを作成 -
v-model
でdata
のプロパティを指定すると、jsファイルのプロパティと描画が連携される - 双方向データバインディングでは入力フォームの値が変わると描画も変わるため、
p
タグの中身もすぐに反映される - 下の例ではinputタグの中にmessageの値がvalueの形で入る
- ユーザーの入力データが自動で更新される
import Vue from 'vue';
new Vue({
el: '#app6',
data: {
message: '双方向データバインディング'
}
})
<div id="app6">
<p>{{ message }}</p>
<input type="text" v-model="message">
</div>
computed 算出プロパティ
-
computed
は関数の処理を書く算出のプロパティである -
methods
と似ているが、computed
はdata
プロパティの変更に依存して描画に反映される - dataやcomputedの値が変化することで自動的に実行
-
computed
はjsの方で算出された値が常に結果がキャッシュされており再利用できる。data
の変更(this
の変更)をthis
で監視しているため、data
プロパティに変更があった場合のみ描画に反映される-
this
を持たないcomputed
はdata
の変更を参照できず、ずっと初回のキャッシュを表示し続ける
-
-
methods
は再描画されるたびに処理が実行される
import Vue from 'vue';
new Vue({
el: '#app7',
data: {
isShow: true
}
computed: {
showString: function() {
return (this.isShow) ? Date.now() : 'isShowはfalse'
},
showString2() {
return Date.now()
}
},
methods: {
showStringMethods() {
return (this.isShow) ? Date.now() : 'isShowはfalse'
},
showStringMethods2() {
return Date.now()
}
}
})
computed
の場合は()
は不要
<div id="app7">
<input type="checkbox" v-model='isShow'>
<p>isShow: {{ isShow }}</p>
<p>showString: {{ showString }}</p>
<p>showString2: {{ showString2 }}</p>
<p>showStringMethods: {{ showStringMethods() }}</p>
<p>showStringMethods2: {{ showStringMethods2() }}</p>
</div>
<!-- 出力結果 -->
isShow: true
showString: 123456789
showString2: 123456789
showStringMethods: 123456789
showStringMethods2: 123456789
<!-- checkbox click -->
isShow: true
<!-- 関数内でthisをを持つため、dataの変更を監視して反映 -->
showString: isShowはfalse
<!-- dataが変更されてもthisがないので反映されず、初回のキャッシュ内容保持 -->
showString12: 123456789
<!-- methodsはdataに依存せず毎回再描画がされるため更新される -->
showStringMethods: isShowはfalse
showStringMethods2: 234567890
v-htmlでサニタイズを無効化
vue.jsでは自動でサニタイズされ、タグは文字列として読み込まれる
タグとして読み込ませたい場合はサニタイズを無効化するv-html
を使う
import Vue from 'vue';
new Vue({
el: '#app10',
data: {
script: '<p style="color:red">タグとして表示</p>'
}
})
<div id="app10">
<p>{{ script }}</p>
<p v-html="script"></p>
</div>
<!-- 出力結果 -->
<p style="color:red">タグとして表示</p>
タグとして表示 (color:red; 適用)
トランジションとアニメーション
アニメーションにはEnter
アニメーションとLeave
アニメーションがある
-
Enter
: 非表示から表示に変わるときのアニメーション- 変化前の状態のclassは
v-enter
, 変化後のclassはv-enter-to
- 変化過程にアニメーションを設定したいときのclassは
v-enter-active
- 変化前の状態のclassは
-
Leave
: 表示から非表示に変わるときのアニメーション- 変化前の状態のclassは
v-leave
, 変化後のclassはv-leave-to
- 変化過程にアニメーションを設定したいときのclassは
v-leave-active
- 変化前の状態のclassは
transition
タグでname
属性を付けた場合はv-
の箇所をその名前に変更できる
import Vue from 'vue';
new Vue({
el: '#app11',
data: {
show: true
}
})
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to { /* .fade-leave-active below version 2.1.8 */
opacity: 0;
}
</style>
<div id="app11">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
コンポーネントの使い方
コンポーネントとは名前付きの再利用可能なVue
インスタンスである(部品のようなもの)
-
Vue.component
でコンポーネントの登録- 第一引数にコンポーネント名を指定する、第二引数にコンポーネントに定義したい値(
data
,template
など)を設定する - コンポーネントで
data
を定義するときは関数で定義する
※ 関数ではなくオブジェクトの形にすると複数のコンポーネントで値を共有する形になる。関数にすることで、個々のコンポーネントに値をもたせることができる
- 第一引数にコンポーネント名を指定する、第二引数にコンポーネントに定義したい値(
-
コンポーネント登録後に
new Vue
でインスタンス化する
// 登録する
Vue.component('sample-component' {
template: '<div>this is sample</div>'
})
// インスタンス生成
new Vue({
el: '#app'
})
コンポーネントを挿入したいところに呼び出すコンポーネントのタグを記述し、app.js
を読み込む
<div id="app">
<sample-component></sample-component>
</div>
<script src="app.js"></script>
HTMLが置換され、次のようになる
<div id="app">
<div>this is sample</div>
</div>
templateについて
temaplate
はコンポーネントのHTMLを渡す
htmlファイルでtempalte
のタグが挿入される
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">clicked {{ count }} times</button>'
})
new Vue({el: '#app12'})
コンポーネント名button-counter
にコンポーネントが割り当てられ、template
のHTMLが挿入される
<div id="app12">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
コンポーネントの登録を省略
slot
を使用していなければ下記のように省略できる
// 省略なし
<template>
<div id="app">
<app-header></app-header>
</div>
</template>
// 省略あり
<temaplte>
<div id="app">
<app-header />
</div>
</template>
単一ファイルコンポーネントを使うとき
// 登録する
Vue.component('sample-component', require('./components/SampleComponent.vue').default);
// インスタンス生成
new Vue({
el: '#app'
})
// template作成
<template>
//
</template>
// このtemplateを使ったときの共通の処理を追記する
<script>
export default {
data: {
//
},
mounted() {
//
}
}
</script>
propsで親コンポーネントから受け継ぐ
-
props
を使うことでコンポーネントの親から子へ値を受け渡せる -
props
は配列の形式で属性を定義し、親コンポーネントからその属性値を受け継いでdata
のプロパティのように振る舞う
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
new Vue({el: '#app13'})
<div id="app13">
<!-- 親コンポーネント(コンポーネントの受け渡し先となる生成元のタグのこと) -->
<!-- このタグを親コンポーネントといい、作成したコンポーネントを子コンポーネントという -->
<blog-post title="title属性をpropsで渡せる"></blog-post>
<blog-post title="title属性をpropsで渡せる"></blog-post>
<blog-post title="title属性をpropsで渡せる"></blog-post>
</div>
オブジェクトでない配列を指定する場合
- 子が親からデータを受け取るとき、配列で指定するか、オブジェクトの配列として指定するかの2通りある
- 簡潔に列挙できるが、データの情報が記述できず、itemもvalueも同様に扱われる
<child
:param0="item",
:param1="value"
>
props: [
'item',
'value'
]
オブジェクトの配列として受け取る場合
- 詳細に記述できるため、違いを明記できる
props: {
item: {
type: Object
},
value: {
type: Number,
default: 0
}
}
emitで子から親へデータを伝達
- 子から親へのデータの伝搬では$emitと$onを一対として使用するカスタムイベントを活用する
- 親コンポーネントからこのイベントを監視する場合は
v-on:
,@
でイベント名を紐付けて使用する
子コンポーネント
<template>
<div class="item">
<span>{{ value }}</span>
<button @click="updateEvt">追加</button>
</div>
</template>
<script>
export default {
data() {
return {
value: 0
}
},
methods: {
plus0ne() {
this.value += 1
// $emitの第2, 第3...引数で変数を指定するとその変数を親が受け取ることができる
this.$emit('update', this.value)
}
}
}
</script>
親コンポーネント
<template>
<counter @updated="updateEvt" />
</template>
<script>
import Child from './Child.vue'
export default {
components: {
Child
},
methods: {
// 子から変数を受けとることができる
updateEvt(value) {
console.log('The value updated' + value)
}
}
}
</script>
イベントとメッセージを親コンポーネントに渡す方法
-
v-on:click=$emit('カスタムイベント名')
のように$emit
で任意のカスタムイベント名を指定することで、そのイベントをイベント名として親子コンポーネントで通知することができる- この例では
v-on:カスタムイベント名=func()
とするとclick
イベントが通知される
- この例では
-
template
はバッククォートで囲むことで階層構造で文字列を書くことができる(ES6より)
Vue.component('blog-post', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-thml="post.content"></div>
</div>
`
})
new Vue({
el: '#app14',
data: {
posts: [
{
id: 1,
title: 'sample post1',
content: '<p>サンプル投稿のコンテント</p>'
},
{
id: 2,
title: 'sample post2',
content: '<p>サンプル投稿のコンテント</p>'
},
{
id: 3,
title: 'sample post3',
content: '<p>サンプル投稿のコンテント</p>'
},
]
}
})
<div id="app14">
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
v-on:enlarge-text="fontSizeScale()"
v-bind:key="post.id"
v-bind:post="post"
></blog-post>
</div>
</div>
</div>