# Vue.js
- 基本
- Vue Instance
- Template Syntax
- Computed Properties and Watchers
- Class and Style Bindings
- Conditional Rendering
- List Rendering
- Event Handling
- Form Input Bindings
- Components Basics
- Component Registration
- Props
- Custom Events
- Slot
- Dynamic & Async Components
- Handling Edge Cases
- Enter/Leave & List Transitions
- State Transitions
- Mixins
- Custom Directives
- Render Functions & JSX
- Plugins
- Filter
- Production Deployment
- Single File Components
- Unit Testing
- Typescript
- Routing
- State Management
- Server Side Rendering
# 基本
# セットアップ
<div id="app" />
var app = new Vue({
el: '#app',
});
# Interpolation (展開)
<span>{{message}}</span>
// index.js
var app = new Vue({
data: {
message: 'hello!',
},
});
# bind
属性にデータをバインドする。バインドしないと、ただのテキストとして評価される。
<a v-bind:href="someUrl" />
<a :href="someUrl" />
new Vue({
data: {
someUrl: 'http://someghing.com/',
},
});
# if
<span v-if="rock">you rock!</span>
new Vue({
data: {
rock: true,
},
});
# for
<li v-for="todo in todos">
{{ todo.text }}
</li>
new Vue({
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something' },
],
},
});
# on
イベントリスナを設定する。
{{message}}
<button v-on:click="onButtonClick" />
<button @click="onButtonClick" />
new Vue({
data: {
message: 'Hello',
},
methods: {
onButtonClick() {
this.message = 'Hello Again!';
},
},
});
# model
two-way binding を設定する。
<input v-model="name" />
new Vue({
data: {
name: 'john',
},
});
# components
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
/>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>',
});
new Vue({
data: {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever' },
],
},
});
# Vue Instance
# インスタンスの作成
const vm = new Vue(options); // root Vue instance
Vue.component('some-component', options); // other Vue instances
# data
- data プロパティに渡したオブジェクトは、Vue の Reactivity System に組み込まれる。
- インスタンスからの変更、元データの変更は、双方向に反映される。
- data の変更は自動的に View に反映される。
- data に後からプロパティを追加することはできない。
var data = { a: 1 };
var vm = new Vue({
data: data,
});
vm.a === data.a; // => true
vm.a = 2;
data.a; // => 2
data.a = 3;
vm.a; // => 3
# 規定のプロパティ・メソッド
インスタンスには、名前が$
で始まる、規定のプロパティとメソッドがある。
vm.$data === data; // => true
vm.$el === document.getElementById('example'); // => true
vm.$watch('a', function(newValue, oldValue) {
// This callback will be called when `vm.a` changes
});
# Lifecycle Hooks
- いくつかのライフサイクルメソッドがある
- ライフサイクルメソッドの中の
this
は、常に Vue インスタンスを指す(アロー関数は使えないので注意する)
# Template Syntax
- テンプレートは Valid な HTML である。
- テンプレートを使わずに、JSX と
render
ファンクションを使うこともできる。
# Interpolation(Vue のデータをテンプレートに展開する)
# Text
- Mustache 記法を使う
<span>Message: {{ msg }}</span>
<span v-once>This will never change: {{ msg }}</span> // 最初の1回のみ更新
<!-- 下記は機能しない。代わりにv-modelを使うこと。 -->
<textarea>{{text}}</textarea>
# Raw HTML
- v-html ディレクティブを使う
<span v-html="rawHtml" />
# Attributes
- 属性の中では Mustache 記法は使えない。代わりに v-bind を使う。
- null, undefined, false の場合、属性はレンダリングされない
<div v-bind:id="dynamicId"></div>
<div v-bind:active="isActive"></div> // falsyならactiveはレンダリングされない
# Javascript
テンプレートには 1 文までの Javascript を記載できる。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
# Directives
v-**
がディレクティブである- ディレクティブには 1 文までの Javascript を記載できる
# modifier
v-on
とv-model
には Modifier という便利なものが用意されている(後述)
# Computed Properties and Watchers
# computed
- 複雑な計算には
computed
プロパティを使う。 comupted
は Getter として機能する。computed
が依存するdata
がアップデートされたときは自動で再計算され、View に反映される
<p>{{ reversedMessage }}</p>
var vm = new Vue({
data: {
message: 'Hello',
},
computed: {
reversedMessage: function() {
return this.message.split('');
},
},
});
# computed と method の違い
computed
と同じことはmethods
でもできる。違いは下記の通り。
computed
は、依存するdata
が変更されない限り再計算をしない(キャッシュが使われる)methods
は、常に再計算をする
# computed と watch の違い
殆どの場合watch
でデータを変更するのは効率が悪い (opens new window)。
computed
を使え。
watch
が最適となるのは、データの変更に応じて、非同期 or 高価な処理を行う場合に限る(参考 (opens new window))
# setter
computed
は、標準では getter としてのみ機能する。
setter を使いたいときは下記のようにする。
computed: {
fullName: {
get: function () {},
set: function (newValue) {}
}
}
# Class and Style Bindings
# Classes
v-bind:class
を使うことで、クラスを動的に設定できる- オブジェクトはインラインでなくても OK(外出ししてもよい)
computed
で計算したオブジェクトを使うと、便利で強力である- Array Syntax で複数の要素を指定することもできる
<div class="some-default-class"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
<!-- errorClass='some-string' -->
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
# Component に Class を指定したとき
Component に Class を指定したときは、そのコンポーネントのルート要素にそのクラスが設定される。
<my-component class="baz boo"></my-component>
# Styles
- Class の場合とほぼ同じ
- Auto Prefix される
<div v-bind:style="styleObject"></div>
<div v-bind:style="[baseStyles, overridingStyles]"></div>
# Conditional Rendering
# v-if
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
# template
2 つ以上の要素にまとめて v-if を設定したいときは template を使う。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
# DOM の再利用
- v-if は、DOM を再利用する。再利用させたくない場合は
key
属性を指定する。 - 下記の場合、label は再利用されるが、input は再利用されない。
<template v-if="loginType === 'username'">
<label>Username</label>
<input key="username-input">
</template>
<template v-else>
<label>Email</label>
<input key="email-input">
</template>
# v-show
v-if との違いは下記の通り
v-if
- 条件が True になったとき、初めて内包する要素が生成される。
- 条件が False になったときは要素が削除される。
- トグルのコストが高い
- あまりトグルしないものに向いている
v-show
- template, v-else は使えない
- 条件に関係なく常に生成される。css の display を変更しているだけ。
- 初期表示のコストが高い
- 頻繁にトグルするものに向いている
# v-if と v-for
v-if
とv-for
を同時に使った場合、v-for
が優先される。- つまり、
v-for
の各子要素に対してv-if
がアタッチされる
# List Rendering
# v-for
- Array や Object にループ処理を行った上でレンダリングするためのもの。
in
はof
に置き換えてもよい。
<!-- Array -->
<li v-for="item in array"></li>
<li v-for="(item, index) in array"></li>
<!-- Object -->
<li v-for="item in object"></li>
<div v-for="(value, key) in object">
<div v-for="(value, key, index) in object">
# key
リスト要素には、必ず key 要素をつけること。 (デフォルトの"in-place patch"という方法を意図的に使いたい場合を除く)
# Array の変更検知
data の Array に対して行った、push()
,pop()
,shift()
,unshift()
,splice()
,sort()
,reverse()
などの変更は、自動的に View に反映される。
filter()
やconcat()
等の元データを変更しないメソッドの場合は、元データを書き換えるのを忘れないこと。
また、下記の操作は Vue が検知できないので注意。
// インデックスを使用した値の変更
vm.items[indexOfItem] = newValue;
// Arrayのlengthの編集
vm.items.length = newLength;
そんなときはVue.set
やsplice
を使うこと
Vue.set(vm.items, indexOfItem, newValue);
vm.items.splice(indexOfItem, 1, newValue);
# Object の変更検知
ルートレベルのプロパティの追加は Vue が検知できない。
var vm = new Vue({
data: {
a: 1,
userProfile: {},
},
});
vm.b = 2; // `vm.b` is NOT reactive
ルートレベルでなければ、set を使うことで追加は可能
Vue.set(vm.userProfile, 'age', 27);
// setではなくObject.Assignなどを使いたいときは、
// 元のオブジェクトは捨てて、フレッシュなオブジェクトをセットすること
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green',
});
# 配列にフィルタ・ソートをかけるには
フィルタ・ソートをかけたいときは、computed
ormethod
を使うと便利。
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
# 指定回数だけ v-for を実行
<span v-for="n in 10">{{ n }} </span>
<!-- 1,2,3,,,,,10 -->
# 複数の要素を繰り返す
v-if と同じく、template を使う。
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
# v-for をコンポーネントで使う
通常の要素と同じように、コンポーネントにも v-for が使える。 ただし、コンポーネント内からは外部の値にアクセス出来ないので、必要なものは明示的に props として渡す必要がある。
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
# Event Handling
# イベントを Listen する
<!-- `counter` is the name of a variable -->
<button v-on:click="counter += 1">Add 1</button>
<!-- `greet` is the name of a method. JSXと異なり、()をつけてもInvokeされない -->
<button v-on:click="greet">Greet</button>
<button v-on:click="greet('hello')">Greet</button>
<!-- イベントを渡したいときは$eventを使う -->
<button v-on:click="greet('hello', $event)"></button>
# Event Modifier
- DOM 専用
.stop
propagation を止める(バブリングフェーズでの外側のイベントの発生を止める).prevent
preventDefault().capture
capture モードにする(キャプチャフェーズでイベントを発生させる).self
ターゲットが自分自身であったときのみイベントを発生させる.passive
passive イベントとして設定(preventDefault しないことを宣言。参考資料 (opens new window))
- DOM/Component で使用可
.once
一度だけ実行
# 注意点
- 適用順に注意
v-on:click.prevent.self
- バブリングを含めた全てのフェーズにおいて prevent する
- ターゲットが自分自身のフェーズにおいてのみ、click イベントが発生する
v-on:click.self.prevent
- ターゲットが自分自身のフェーズにおいてのみ click イベントが発生かつ prevent する
- 当然だが、
prevent
とpassive
は同時に使用できない
# Key Modifier
<input v-on:keyup.13="submit">
<!-- same as above -->
<input v-on:keyup.enter="submit">
エイリアスの一覧
.enter
.tab
.delete
(captures both “Delete” and “Backspace” keys).esc
.space
.up
.down
.left
.right
KeyboardEvent.key (opens new window)をケバブケースにしたものも使用可能。
<input @keyup.page-down="onPageDown">
独自のエイリアス設定も可能
Vue.config.keyCodes.f1 = 112;
# System Modifier Key
.ctrl
.alt
.shift
.meta
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
# exact Modifier
exact
を指定すると、指定したキーのみが押されているときのみイベントが発生する。
<!-- this will fire even if Alt or Shift is also pressed -->
<button @click.ctrl="onClick">A</button>
<!-- this will only fire when Ctrl and no other keys are pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>
# Mouse Button Midifier
.left
.right
.middle
# Form Input Bindings
# 基本
v-model
を使う- IME 環境では確定されるまで data は変更されない。確定前の入力を捕捉したい場合は
input
イベントを使って自前で実装すること。
# Text
<input v-model="message">
# Multiline text
<textarea v-model="message"></textarea>
# Checkbox
<!-- checkedはbooleanになる -->
<input type="checkbox" id="checkbox" v-model="checked">
<!-- もし、toggleに特定の文字列を入れたい場合 -->
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
<!-- checkedNamesは、valueの値からなる配列になる -->
<div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<span>Checked names: {{ checkedNames }}</span>
</div>
# Radio
picked
には value の値が入る。
<input type="radio" id="one" value="One" v-model="picked">
<input type="radio" id="two" value="Two" v-model="picked">
# Select(単一選択)
selected
にはoption
で囲んだ値が入る- iOS で問題が起こるので、1 行目は
disabled
とし、空の value を設定したほうがよい
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
# Select(複数選択)
<!-- selectedはArrayになる -->
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
# その他
value
の値は、string 以外にも、v-bind した値を使用することもできる。この場合、string だけでなく、オブジェクトや数値を渡すことができる。
# Modifier
.lazy
input イベントではなく change イベント(フォーカスを失った時)の際に data を更新する。.number
文字列ではなく数値として扱う.trim
余分な空白等を削る
<input v-model.lazy="msg" >
<input v-model.number="age" type="number">
<input v-model.trim="msg">
# Components Basics
# 基本
コンポーネントは、root Vue とほぼ同じプロパティを持つ。相違点は次のとおり。
el
がないdata
はファンクションにする必要がある
# 注意点
コンポーネントはシングルルート要素でなければならない
template リテラルは IE では使えないので注意。使うなら babel でトランスパイルする。
template = ` multiline `;
# global と local
global に宣言すると、root Vue instance の中のどこからでも使える。
Vue.component('my-component-name');
# props
コンポーネントはコンポーネントの外の値にアクセスできない。 アクセスするには、props を使って明示的に値を渡す必要がある。
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>',
});
# Emitting Event
コンポーネントから親にイベントを渡すには、this.$emit
を使う。
<!-- component -->
<button v-on:click="$emit('enlarge-text')"></button>
<button v-on:click="$emit('enlarge-text', 2)"></button>
<!-- parent -->
<!-- 引数があり、かつメソッドの()を省略した場合、自動的に第一引数に渡される -->
<blog-post :enlarge-text="alert()"></blog-post>
<blog-post :enlarge-text="alert($event)"></blog-post>
<blog-post :enlarge-text="onEnlargeText"></blog-post>
# コンポーネントで v-model を使うには
v-bind は、内部的には次の 2 つの機能から成り立っている。
value
prop へのバインディングinput
イベントによるデータの更新
<input v-model="searchText">
<!-- これは下記と等価 -->
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
コンポーネントの場合は、上記を念頭に置き、下記のようにする。
<!-- parent -->
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
<!-- component -->
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
<!-- ここまで来たら、parentは下記の通り書き換えてもOK -->
<custom-input v-model="searchText" />
なお、標準では、v-model は、value
props とinput
event を使うので、
checkbox などを使うときは、下記のような工夫が必要。
Vue.component('base-checkbox', {
model: {
// v-modelに、valueの代わりに`checked`を使え、と伝える
prop: 'checked',
// v-modelに、inputイベントの代わりに`change`イベントを見ろ、と伝える
event: 'change',
},
props: {
checked: Boolean,
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`,
});
# Slot
React での children と同じ。
<!-- parent -->
<alert-box>
Something bad happened.
</alert-box>
<!-- component -->
<div>
<slot></slot> <!-- ここに`Something bad happened.`が入る -->
</div>
# Dynamic Components
- コンポーネントを動的に切り替えたい場合は、
component
要素とis
属性を使う。 is
の値には次のいずれかを指定する- 登録済みのコンポーネントの名称を入れた変数
- コンポーネントを作成するときの
option
に相当するオブジェクト
<component :is="currentTabComponent"></component>
# DOM テンプレートパース時の警告
ul
,ol
,table
のようないくつかの HTML 要素には、それらの要素の中でどの要素が現れるかに制限がある。そんなときはis
属性を使うこと。- なお、下記の中であればこの制約は該当しない
template
プロパティの中.vue
ファイルの中<script type="text/x-template">
の中
<!-- 下記は認められない -->
<table>
<blog-post-row></blog-post-row>
</table>
<!-- 下記のようにすべし -->
<table>
<tr is="blog-post-row"></tr>
</table>
# Component Registration
# 名前の付け方
- 全て小文字、必ずハイフンを含める(W3C)
- PascalCase で宣言すると、ケバブ、パスカルのどちらでもアクセスできる。ただし、DOM の中ではケバブのみが valid である点に留意する
# Global Registration
Vue.component('component-a', {});
Vue.component('component-b', {});
component-b から component-a を利用できる。
# Local Registration
var ComponentA = {};
var ComponentB = {};
new Vue({
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
});
component-b から component-a は利用できない。利用するには下記のようにする。
var ComponentA = {};
var ComponentB = {
components: {
'component-a': ComponentA,
},
};
// もしくは、webpack等を使っている場合、ComponentB.vueにおいて
import ComponentA from './ComponentA.vue';
export default {
components: {
ComponentA,
},
};
# Base Component
- 頻繁に使用するコンポーネントは Base Component として作成しておくと良い。
- Base コンポーネントを自動的にグローバル登録する方法 (opens new window)もある
# Props
# camel vs kebab
props の名前を camelCase にした場合は、props を渡す時に kebab-case にする必要がある。 ただし、string templates の中ではこの制約は該当しない。
Vue.component('blog-post', {
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>',
});
<!-- DOMテンプレート -->
<blog-post post-title="hello!"></blog-post>
<!-- string templateの中では下記でもOK -->
<blog-post postTitle="hello!"></blog-post>
# Prop Types & Validation
Prop Types を使いたいときは、配列ではなく、オブジェクトで指定する。
Vue.component('my-component', {
props: {
// Basic type check (`null` matches any type)
propA: Number,
// Multiple possible types
propB: [String, Number],
// Required string
propC: {
type: String,
required: true,
},
// Number with a default value
propD: {
type: Number,
default: 100,
},
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function
default: function() {
return { message: 'hello' };
},
},
// Custom validator function
propF: {
validator: function(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].indexOf(value) !== -1;
},
},
},
});
Type として使えるもの
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- コンストラクタ関数(instanceof でチェックされる)
# props に様々な種類のデータを渡す
v-bind を使うと、様々な Javascript の値を渡すことができる。
<!-- 数値として -->
<blog-post :likes="42"></blog-post>
<!-- オブジェクトの値を渡す -->
<blog-post :likes="post.likes"></blog-post>
<!-- Booleanとして -->
<blog-post :is-published="false"></blog-post>
<!-- 配列として -->
<blog-post :comment-ids="[234, 266, 273]"></blog-post>
<!-- オブジェクトとして -->
<blog-post :author="{ name: 'Veronica', company: 'Veridian Dynamics' }"></blog-post>
# オブジェクトのプロパティを分解して渡す
v-bind=
を使うことで全てのプロパティを分解して渡せる。
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>
<!-- 上記は下記と等価 -->
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
# One-way data flow
いかなる場合でも、props の値は変更するな。
変えたいなら、あくまでdata
の初期値として利用するにとどめるか、computed を使うなどしろ。
# コンポーネントに対して Props に記載してない属性を渡すとどうなる?
- コンポーネントのルート要素にアタッチされる。
- その際、class と style についてはマージされる。それ以外はまるごと置換えられるので注意。
この機能を無効にするには:
Vue.component('my-component', {
inheritAttrs: false,
});
無効にしても、$attrs
を使うことで属性の取得は行える。これは、Base Component を作る時に特に便利。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
:value="value"
:input="$emit('input', $event.target.value)"
>
</label>
`,
});
<base-input
label="something" // `label` propsになる
v-model="username" // `value` propsになる。また、inputイベントを受けて値を更新する。
class="username-input" // input要素にアタッチされる
placeholder="Enter your username" // input要素にアタッチされる
/>
# Custom Events
# イベント名
イベント名には常に kebab-case を使え。例外はない。
# ネイティブイベントを補足する
.native
modifier を使うと、コンポーネントのルート要素のイベントを捕捉できる。- ルート要素以外のイベントを捕捉するには工夫 (opens new window)が必要。
<base-input v-on:focus.native="onFocus"></base-input>
# .sync
modifier
- 親とコンポーネントの間で擬似的な two-way binding を行うためのもの。
- キモは
update:
の記法と.sync
がセットになっていること。
// コンポーネント側
this.$emit('update:title', newTitle);
<!-- 親 -->
<text-document
:title="doc.title"
@update:title="doc.title = $event"
></text-document>
<!-- 上記は下記と等価 -->
<text-document :title.sync="doc.title"></text-document>
# Slot
# Scope
親テンプレート内の全てのものは親のスコープでコンパイルされ、子テンプレート内の全てものは子のスコープでコンパイルされる
# Named Slot
<!-- component -->
<div class="container">
<header>
<slot name="header">Default Content</slot>
</header>
<main>
<slot>Default Content</slot>
</main>
<footer>
<slot name="footer">Default Content</slot>
</footer>
</div>
<!-- parent -->
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>
# Scoped Slots
slot-scope
属性を指定することで、slot(子)の属性に、外側(親)からアクセスできる。
<!-- component -->
<slot :a="1" normalAttr="2"></slot>
<!-- parent -->
<todo-list>
<p slot-scope="slotProps">{{ slotProps }}</p>
<!-- { "a": 1, "normalAttr": "2" } -->
</todo-list>
# Dynamic & Async Components
# Dynamic Component(keep-alive)
is
プロパティを使ってコンポーネントを切り替えると、切り替える前のコンポーネントは破棄される。 破棄したくない場合は、keep-alive
要素で囲むこと。- この機能は、コンポーネントが name を持っている場合にのみ機能するので注意すること
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
# Async Components
コンポーネントを非同期に作成する方法
Vue.component('async-example', option);
// 方法1(ファンクションを使う)
const option = function(resolve, reject) {
setTimeout(function() {
resolve({
template: '<div>I am async!</div>',
});
}, 1000);
};
// 方法2(Webpack の code-splitting の機能 を使用)
const option = function(resolve) {
require(['./my-async-component'], resolve);
};
// 方法3(Promiseを使う方法。importはPromiseを返す)
const option = () => import('./my-async-component');
# Handling Edge Cases
# 親へのアクセス
下記のような便利な変数もあるが、デバッグ目的でのみ使うこと。
$root
root instance の値にアクセス$parent
親の値にアクセス
# 子へのアクセス
ref を使用することで、要素への参照を取得できる
<input ref="usernameInput"></input>
this.$refs.input.focus();
コンポーネントの中の要素への参照を取得したい場合はこちら (opens new window)を参照
# Dependency Injection
React の Context に近い。親コンポーネントのデータを、子・孫コンポーネントで使いたい時に使う。多用厳禁(Vuex を使え)。
// 親コンポーネント
provide: function () {
return {
getMap: this.getMap
}
}
// 子 or 孫コンポーネント
inject: ['getMap']
# Programatic Event Listener
$emit
イベントを発生させる$on
イベントを Listen する$off
イベントの Listen をやめる$once
イベントを一度だけ Listen する($off
がいらない?)
これらをうまく使うとコードをきれいに書ける場合がある。
例:
mounted: function () {
this.attachDatepicker('startDateInput')
this.attachDatepicker('endDateInput')
},
methods: {
attachDatepicker: function (refName) {
var picker = new Pikaday({
field: this.$refs[refName],
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
}
# 循環参照の解決
循環参照 = 互いに依存するコンポーネント
Vue.component
を使ってグローバル登録した場合は Vue が自動的に問題を解消するが、webpack を使っている場合は下記のエラーが出る。
Failed to mount component: template or render function not defined.
これを解決するには、親となるコンポーネントで次のようにする(詳細 (opens new window))。
// $optionsを使う方法
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}
// webpackのimportを使う方法
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
}
# テンプレートの作り方(番外編)
どちらも使うな
- Inline Template
- X-Template
# Enter/Leave & List Transitions
# 単一の要素・コンポーネントのトランジション
transition
コンポーネントでラップすると、entering
とleaving
のトランジションを設定できる。ラップできる要素 or コンポーネントの条件は下記の通り。
v-if
が使われているv-show
が使われている- Dynamic Component である
- Component root nodes である
<transition>
<p v-if="show">hello</p>
</transition>
.v-enter,
.v-leave-to {
transform: translateX(10px);
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s;
}
transition
で囲まれた要素が挿入・削除されたときに、次のことが起こる。
- CSS クラスが適切なタイミングで挿入される
- JavaScript hooks が適切なタイミングで実行される
- アニメーション・トランジションも、JS Hooks もない場合は、DOM 操作が即時に実行される。
# transition classes
6 種類の class が付与される。
v-enter
,v-leave
最初の 1 フレームだけに付与されるv-enter-to
,v-leave-to
2 フレーム目から最後まで付与されるv-enter-active
,v-leave-active
最初から最後のフレームまで付与される。duration, delay, easing curve の設定に使う。
v-
の部分は、transition
要素の name 属性によって変わる。
<transition>
の場合はv-
, <transition name="my">
ならmy-
になる。
# CSS Transitions を使った例
前述の通り
# CSS Animations を使った例
.v-enter-active {
animation: bounce-in 0.5s;
}
.v-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
# カスタムクラス
Animate.css (opens new window) などのライブラリを使うときは、transition
要素に下記の属性をつけることで、カスタムクラスを設定して対応する。
enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class
move-class
<transition
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
# animation と transition の同時利用
同時利用するときは Vue がアニメーションの終わりを判定できないので、個別に設定 (opens new window)が必要。
# Explicit Transition Durations
Vue はアニメーションやトランジションの終了をtransitionend
or animationend
イベントで判定する。いくつかのケースでこれが不適切な場合があるので、そういうときは明示的に時間を指定する。
<transition :duration="1000" />
<transition :duration="{ enter: 500, leave: 800 }" />
# Javascript Hooks
Velocity.js (opens new window)など、JS ベースのアニメーションライブラリを使うときは、Javascript Hooks を使う。詳細はこちら (opens new window)。
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
/>
const option = {
methods: {
beforeEnter: function(el) {},
enter: function(el, done) {},
afterEnter: function(el) {},
enterCancelled: function(el) {},
beforeLeave: function(el) {},
leave: function(el, done) {},
afterLeave: function(el) {},
leaveCancelled: function(el) {},
},
};
# 初期表示時のトランジション
通常、初回表示の際はトランジションが適用されないが、appear
属性をつけることでトランジションを適用することができる。その他、細かい設定 (opens new window)も可能。
<transition appear></transition>
# 複数の要素のトランジション
transition
の中で複数の要素を扱うときは、それぞれにkey
属性を設定すること。そうしないと、DOM が再利用されてトランジションが適用されない。
<transition>
<button v-if="docState === 'saved'" key="saved">
Edit
</button>
<button v-if="docState === 'edited'" key="edited">
Save
</button>
<button v-if="docState === 'editing'" key="editing">
Cancel
</button>
</transition>
# Transition Mode
2 つ以上の要素にトランジションをかける時、どの順番でトランジションするかを設定できる。指定しなかった場合は、同時に実行される。
out-in
古い要素のトランジションが完了後、新しい方のトランジションを開始in-out
上記の逆。あまり使わない。
<transition name="fade" mode="out-in">
# 複数のコンポーネントのトランジション
複数のコンポーネントのトランジションには、ダイナミックコンポーネントを使用する。key
属性は不要。
<transition>
<component v-bind:is="view"></component>
</transition>
new Vue({
data: {
view: 'v-a',
},
components: {
'v-a': {
template: '<div>Component A</div>',
},
'v-b': {
template: '<div>Component B</div>',
},
},
});
.v-enter-active,
.v-leave-active {
transition: opacity 0.3s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
}
# リスト要素のトランジション
v-for
などにトランジションを設定するには<transition-group>
を使用する。
transition
と異なり、transition-group
は実際に要素をレンダリングする。デフォルトはspan
なので変更したいときはtag
属性を指定する。- Transition Mode は使えない
- リストの各要素に
key
属性が必須 name
属性でクラス名(v-
の部分)が変わる挙動は、transition
と同じ。
# List Entering & Leaving Transitions
<transition-group tag="div">
<span v-for="item in items" v-bind:key="item" class="list-item">{{ item }}</span>
</transition-group>
.v-enter-active,
.v-leave-active {
transition: all 1s;
}
.v-enter, .v-leave-to /* .list-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(30px);
}
# リスト要素の移動のトランジション
v-move
クラスを使う。他のクラスと同じく、transition-group
のname
属性によりルックアップするクラス名が変わる。また、move-class
属性を指定することでクラス名をカスタマイズすることも可能。
.v-move {
transition: transform 1s;
}
.v-leave-active {
position: absolute;
}
注意点
inline
では使えない。inline-box
orflex
を使うこと。- 要素の削除時 =
v-leave-active
時は position を absolute にしないと、v-move
が効かない。詳細はサンプルを参照。
# Staggering List Transitions
時間差でリストを畳んだり表示したりする。JavaScript で制御する。詳細はドキュメント (opens new window)参照。
# Transition の再利用
slot
を使って再利用できる Transition を作ると便利。詳細はドキュメント (opens new window)参照。
# Dynamic Transitions
- トランジションの
name
属性を動的に変更することで、トランジションをダイナミックにすることができる。 - もしくは、Javascript Hooks を使う。
# State Transitions
数値のトランジションには Tween.js, 色のトランジションには Color.js などが使える。必要になったときにドキュメント (opens new window)を参照する。
# Mixins
mixin = コンポーネント作成時につかうoption
の雛形
var myMixin = {
created: function() {
this.hello();
},
methods: {
hello: function() {
console.log('hello from mixin!');
},
},
};
var MyComponent = new Vue.extend({
mixins: [mixin],
// ...
}
# マージ戦略
data
sharrow merge (1 階層目だけ)される。重複時は mixin 側が破棄されるLifecycle method
Array になる。全て保持される(mixin の方が最初に実行される)methods
,components
,directives
重複時は mixin 側が破棄される
マージ戦略のカスタマイズ方法はこちら (opens new window)
# Global Mixin
使うな
Vue.mixin({
/*...*/
});
# Custom Directives
自分だけのv-***
を作ることができる。詳細はこちら (opens new window)。
// v-focus を作りたい場合
// グローバル
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
// ローカル(コンポーネントごと)
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
# Render Functions & JSX
TODO
# Plugins
プラグインのタイプ
- グローバルなメソッドとプロパティを追加する
- グローバルなアセットを追加する(directives/filters/transitions etc)
- コンポーネントの option を mixin で追加する(e.g. vue-router)
- Vue instanse メソッドを追加する(Vue.prototype を使う)
- 上記のいずれかと組み合わせて、API を追加する(e.g. vue-router)
プラグインの使い方
Vue.use(MyPlugin, options);
new Vue({});
# Filter
Filter は、Mustache 記法の中と、v-bind の中で使える。
<!-- in mustaches -->
{{ message | capitalize }}
<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>
宣言方法
// ローカル
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
// グローバル
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({})
なお、Filter に引数を渡した場合は、それらは function の第2引数以降に渡される。
{{ message | filterA('string', someValue) }}
<!-- function (message, arg1, arg2)) -->
# Production Deployment
# Single File Components
webpack+vue-loader
により、シングルファイルコンポーネントを利用することで、次のことが可能になる。
- シンタックスハイライト
- テンプレートエンジンの利用
- プリプロセッサの利用
- scoped CSS の利用 など
# Unit Testing
# Typescript
# コンポーネントの Type
コンポーネントにタイプをアタッチするには、Vue.component
or Vue.extend
でコンポーネントを作ること。
# Class-Style Vue Components
vue-class-component (opens new window)を使えば Vue コンポーネントをクラスで記載できる。
# Routing
vue-router (opens new window)を使え
# State Management
vue-devtools を使えばタイムトラベルデバッグもできる
# Server Side Rendering
もしくはこちらのガイド (opens new window)を参照して自前でやれ