概要
Vue.jsを初歩から実践まで徹底的に解説。使いやすくかつ,プロダクションでも活躍するVue.jsをVue.jsコントリビューターの著者らが解説する一番わかりやすい入門書です。小規模な適用例やjQueryからの移行サンプルに加え,大規模開発を想定したアプリケーション開発も体験できます。Vue.jsはGitHubで2017年最も人気のあったJavaScriptフレームワークに選ばれるなど大注目の技術です。
こんな方におすすめ
- Vue.jsに興味のあるWebエンジニア
- Vue.jsを本番で使いたいが課題があるエンジニア
- Vue.jsを動かしてみたいWebデザイナー,コーダー
サポート
ダウンロード
(2020年8月12日更新)
以下のファイルをダウンロードできます。
圧縮ファイルをダウンロードして,適宜展開してご利用ください。
2019年9月9日,動作しなかったコードを修正しています。ダウンロードしなおしてください。
- ダウンロード
- sample20200811.zip
正誤表
本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。
P.96 3.3.1のコンポーネントの表記例
P.135 # 4.4.5のグローバルメニューにログアウトメニュー・ログインメニューを追加したあとの解説
本来はグローバルメニューにログアウト・ログインメニュー実装後に下記のようにログイン関連の説明を追加すべきでした。
正 |
上記グローバルメニューのHTML内で認証モジュールAuthを利用するために、Vueインスタンス生成時のdataプロパティにAuthを指定しましょう。
var app = new Vue({
data: {
Auth: Auth
},
router: router
}).$mount('#app')
|
P.107 # 3.4.3のコンテンツを埋め込まずにslotを使った場合の画像例
画像が誤っていました。文言は同一ですがスタイルが一部異なります。
P.107 # 3.4.3のコンテンツを埋め込んだ場合の画像例
画像が誤っていました。「りんご、イチゴ」と表記されるべき部分が「いちご、りんご」となっていました。
P.167 # 5.2.3の「スコープ付きスロット」の例のHTML側の表記
slot-scopeを使わないもの,使うものそれぞれ動作しますが,:keyの指定が抜けていました。
<li slot-scope="slotProps" v-if="slotProps.todo.isCompleted" :key="slotProps.todo.id">
|
<li slot-scope="{ todo }" v-if="todo.isCompleted" :key="todo.id">
|
P.221 # 6.6.3の「名前付きスタイル識別子の算出プロパティ」の実行
vue serveに指定するファイル名が誤っていました。正しくはform.vueを開く箇所でした。
$ editor form.vue
$ vue serve form.vue --open
|
P.196 # 5.5.2のコード,authに関する記述
初出の$optionsに対する説明のコメントが不足していました。
誤 |
var auth = this.$options.auth
|
正 |
var auth = this.$options.auth // $optionsでVueインスタンス生成時のオプションを参照できます
|
P.354〜P.355 # 10.2.1 kbnButtonコンポーネントの単体テストコード(disabledプロパティの値がfalseのテストコード)
本来はテストコードでpropsDataを指定しなければいけない箇所で指定していませんでした。直前のテストコードでtrueにしているのを参照してください。
誤 |
describe('false', () => {
it('disabled属性が付与されていないこと', () => {
const button = mount(KbnButton)
expect(button.attributes().disabled).to.be.an('undefined')
})
})
|
正 |
describe('false', () => {
it('disabled属性が付与されていないこと', () => {
const button = mount(KbnButton, {
propsData: { disabled: false }
})
expect(button.attributes().disabled).to.be.an('undefined')
})
})
|
P.69 # 2.10.1のライフサイクルフック一覧表
表中の表記が誤っていました。
P.96 # 3.3.1のコンポーネントの書式の誤り
defaultとすべき箇所が,defalutと誤っていました。
正しくは以下の通りです。
Vue.component(コンポーネント名,{
props: {
親から受け取る属性名:{
type: StringやObjectなどのデータ型,
default: デフォルト値,
required: 必須かどうかの真偽値,
validator: バリデーション用の関数
}
}
// ...template内で「親から受け取る属性」が使える
})
|
P.193 # 5.5.1の説明文
誤 |
Consoleの出力から、ミックスイン→コンポーネントの順番フック関数が呼ばれていることが分かります。
|
正 |
Consoleの出力から、ミックスイン→コンポーネントの順番でフック関数が呼ばれていることが分かります。
|
P.107 # 3.4.3の最後のJSFiddleのURL
本来挿入されるべき,Vue.jsの読み込みが行われていませんでした。
誤 |
https://jsfiddle.net/bha18soe/
|
正 |
https://jsfiddle.net/upd9843j/
|
P.182,P.183 # 5.4.3のデータオブジェクト中のソースコード
P.182の下段からP.183中段にかけての,my-buttonのコードに不足があり動作しませんでした。正しくは下記のコードです。
正 |
new Vue({
el: '#app',
render: function (createElement) {
return createElement('my-button', {
attrs: {
href: 'https://vuejs.org/'
},
props: {
tag: 'a'
}
}, 'anchor')
},
components: {
MyButton: MyButton
}
})
|
正 |
new Vue({
el: '#app',
render: function (createElement) {
return createElement(MyButton, {
attrs: {
href: 'https://vuejs.org/'
},
props: {
tag: 'a'
}
}, 'anchor')
}
})
|
(以下,2019年2月4日更新)
P.200 # 6.1.1冒頭のVue CLIに関する解説のページ
誤 |
Vue CLIは、Vue.js向けのアプリケーション開発環境をセットアップなどの機能を提供する公式のコマンドラインツールです。
|
正 |
Vue CLIは、Vue.js向けのアプリケーション開発環境セットアップなどの機能を提供する公式のコマンドラインツールです。
|
P.331 # 9.2.1のディレクトリ作成
誤 |
$ mkdir -p src/ components/{ atoms, molecules, organsms, templates}
|
正 |
$ mkdir -p src/ components/{ atoms, molecules, organisms, templates}
|
P.346 # 9.4.1最初の構成図
図中の番号表記が誤っていました。正しくは下記です。
(以下2018年11月14日更新)
P.123 # 4.4の最初のコードブロック
誤 |
<!-- router-link による ナビゲーション 定義 -->>
|
正 |
<!-- router-link による ナビゲーション 定義 -->
|
P.125 # 4.4.2の説明文
誤 |
UserListコンポーネントのテンプレートとJavaScriptを回収していきます。
|
正 |
UserListコンポーネントのテンプレートとJavaScriptを改修していきます。
|
P.215 6章の「コラム スコープ付きCSSのメリット」のコードのbar.vue
コードが誤っていました。正しくは下記のコードです。
<template>
<div class="bar">
<h1 class="header">Barコンポーネント</h1>
<p>これはBarコンポーネントです。</p>
<foo/> <!-- Fooコンポーネントの利用。scriptブロックも要注目 -->
</div>
</template>
<script>
import Foo from './foo'
export default { // FooコンポーネントをBarコンポーネントに登録
components: {
Foo
}
}
</script>
<style scoped>
.bar {
border: 1px solid red;
margin: 4px;
padding: 4px;
}
.header { font-size: 125%; }
</style>
|
P.280~281 # 7.6.1 の最後のコードブロックのコード例
actionsとすべき個所がactionになっていました。正しくは下記のコードです。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
// fooモジュール
foo: {
state: {
value: 123
},
actions: {
log (ctx) {
console.log('モジュールfooのステート', ctx.state)
}
}
},
// barモジュール
bar: {
state: {
message: 'Hello!'
},
actions: {
log (ctx) {
console.log('モジュールbarのステート', ctx.state)
}
}
}
}
})
// logアクションを呼び出す
// fooモジュール内のステートと、barモジュール内のステートが出力される
store.dispatch('log')
|
P.358 # 10.2.2の
test/unit/specs/components/molecules/kbnLoginForm.spec.js中のdescribeに与える文言
メールアドレス形式のフォーマットを検証する部分で,itの第一引数に与える説明が誤っていました。
誤 |
describe('メールアドレス形式のフォーマット', () => {
it('validation.email.requiredがvalidであること', () => {
|
正 |
describe('メールアドレス形式のフォーマット', () => {
it('validation.email.formatがvalidであること', () => {
|
P.371 # 10.3.1のコード中のコメント
(以下2018年10月10日更新)
P.299 8.4.1のvue init時の引数の文字列
誤 |
$ vue init webpack kandan-app
|
正 |
$ vue init webpack kanban-app
|
(以下2018年10月3日更新)
P.305 8.5冒頭の箇条書き
誤 | ・複数のHTMLテンプレート
・CSSやSassなどのスタイルシート
・JavaScriptファイルによって
|
正 |
・HTMLテンプレート
・CSSやSassなどのスタイルシート
・JavaScript
|
P.308の脚注部を除く最下行 8.6.1のKarmaのインストールに関する説明
文章が途中で抜けていました。
誤 |
Karma公式でサポートする
|
正 |
Karma公式でサポートするKarmaランチャ
|
(以下2018年9月21日更新)
P.2 1章のプロジェクトに関する説明
誤 |
プロジェクトして開発を行っています。
|
正 |
プロジェクトとして開発を行っています。
|
P.10 1.2.1のコード例に関する説明
誤 |
まだ文法事項を解説してないので、
|
正 |
まだ文法事項を解説していないので、
|
P.11の5行目 1.2.1の説明
誤 |
Vue.jsは先に上げた現代のWebフロントエンド開発の
|
正 |
Vue.jsは先に挙げた現代のWebフロントエンド開発の
|
P.18 1.5.1の単一ファイルコンポーネントのコード例
templateの閉じタグが誤っていました。
誤 |
<template>
<p>{{message}}!</p>
<template>
<script>
export default = {
data () {
return {
message: 'こんにちは'
}
}
}
</script>
<style scoped>
p {
color: red;
}
</style>
|
正 |
<template>
<p>{{message}}!</p>
</template>
<script>
export default = {
data () {
return {
message: 'こんにちは'
}
}
}
</script>
<style scoped>
p {
color: red;
}
</style>
|
P.22 1.6の冒頭の説明
誤 |
ユーザーのWebアプリケーションを開発をサポートするために
|
正 |
ユーザーのWebアプリケーション開発をサポートするために
|
P.23 1.6のVue Curatedの説明
誤 |
Vue.jsコアチームが激選したプラグイン、
|
正 |
Vue.jsコアチームが厳選したプラグイン、
|
P.34 2.3 Vueオブジェクトの説明文
誤 |
基本機能を紹介してきます。
|
正 |
基本機能を紹介していきます。
|
P.47 2.7の実行結果の画面
実行結果画面が誤っていました。正しくは下記画面です。画像をクリックすると大きく表示できます。
P.55 2.9.2のstyleの記述例を説明した文中のコード表記例
誤 |
styleはセミコロン("collor: tomato; background: yellow")で
|
正 |
styleはセミコロン("color: tomato;; background: yellow")で
|
P.71 2.10.4のコード例中のコメント
P.73 コラム 算出プロパティのキャッシュ機構の最初の説明
誤 |
ここで説明したメソッドと先程の算出プロパティは、いずれも関数の形と取るという点では同じで、
|
正 |
ここで説明したメソッドと先程の算出プロパティは、いずれも関数の形を取るという点では同じで、
|
P.85 3.2.1 グローバルコンポーネントの定義内のオプション表のfilters行
誤 |
データを文字列と整形する
|
正 |
データを文字列に整形する
|
P.96 3.3.1のフルーツの名前をリストするコンポーネント中のコメント
誤 |
<!-- v-forで繰り返した各furitをprops(fruits-item)に与えている -->
|
正 |
<!-- v-forで繰り返した各fruitをprops(fruits-item)に与えている -->
|
P.104 コラム Atomic DesignのOrganisms中の例の説明
誤 |
ログインフォームやコメントフォームなナビゲーションバーなど。
|
正 |
ログインフォームやコメントフォーム、ナビゲーションバーなど。
|
P.108 3.4.4 箇条書きの項目名
P.118の3行目 4.2.2の「HTML側の指定とページ遷移の実行」のrouter-viewなどの説明箇所
誤 |
〜部分にレンダリングされます。ページを開いてからページ遷移が
|
正 |
〜部分にレンダリングされます。しかし、ページを開いてからページ遷移が
|
P.123 4.4の冒頭の解説
誤 |
例として、ユーザー情報登録・閲覧が可能なアプリケーション用います。
|
正 |
例として、ユーザー情報登録・閲覧が可能なアプリケーションを用います。
|
P.123 4章の脚注7
誤 |
ES2015以降でプログラムを作成したい場合もWebpackの利用はほぼ必須です。
|
正 |
ES2015以降でプログラムを作成したい場合もwebpackの利用はほぼ必須です。
|
P.124 4.4リスト中のライブラリの読み込み部分
動作はしますがバージョンが古いものでした。
誤 |
<script src="https://unpkg.com/vue@2.4.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@2.7.0/dist/vue-router.min.js"></script>
|
正 |
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
|
P.136 4.4.6のリスト中のvue-routerのバージョン指定
誤 |
<script src="https://unpkg.com/vue-router@3.1.0"></script>
|
正 |
<script src="https://unpkg.com/vue-router@3.0.1"></script>
|
P.147 「コラムVue Routerを使った大規模なアプリケーションの実装」の表記
誤 |
propを使ったデータの受け渡しチェーン
|
正 |
propsを使ったデータの受け渡しチェーン
|
P.151 5.1.3の記述
誤 |
実際にコードと見ていきます。
|
正 |
実際にコードを見ていきます。
|
P.160 5.1.4最後のアニメーション関連の文章
誤 |
Velocity.js,jQueryのアニメーション機能、
|
正 |
Velocity.js、jQueryのアニメーション機能、
|
P.161 5.2のスロットの説明
誤 |
プロパティでテキストを渡して表示されても
|
正 |
プロパティでテキストを渡して表示しても
|
P.168 5.3のビルトインのディレクティブとの比較解説
誤 |
Vue.jsのアプリケーションを実装する上で、ビルドインの
|
正 |
Vue.jsのアプリケーションを実装する上で、ビルトインの
|
P.168 5.3のローカルディレクティブの利用例
誤 |
ブログエントリの要素付けのUIなどです。
|
正 |
ブログエントリのタグ付けのUIなどです。
|
P.170 5.3.1の画像の説明の表記
誤 |
すると以下のようにnoimageの画像が
|
正 |
すると以下のようにno imageの画像が
|
P.172 5.3.3の「updateフックによる値の変更の検知」内のoldValueプロパティの説明
誤 |
そこで、bindingのvalue、oldValueプロパティの値を比較して...
|
正 |
そこで、bindingのvalue、updateとcomponentUpdatedフックで利用できる変更前の値oldValueプロパティを比較して...
|
P.174 5.3.4のコード中のbindの表記
本来「:」とすべき個所が「!」になっていました。
誤 |
bind! function (el, binding) {
|
正 |
bind: function (el, binding) {
|
P.175 5.3.4のコード中のupdateとdataの表記
本来「:」とすべき個所が「!」になっていました。
誤 |
update! function (el, binding) {
|
正 |
update: function (el, binding) {
|
P.175 5.3.4のnoImageURLプロパティに関する記述
誤 |
文字列をセットして、属性値と指定します。
|
正 |
文字列をセットして、属性値を指定します。
|
P.176 5.3.4の何も表示されない状態に関する記述
誤 |
no_image画像も何も表示されなくなるはずです。
|
正 |
no image画像も何も表示されなくなるはずです。
|
P.182 5.4.3の「データオブジェクト」コード中のrenderの表記
誤 |
render! function (createElement) {
|
正 |
render: function (createElement) {
|
P.192 5.5.1のmixinsプロパティの説明
誤 |
コンポーネントオプジョンのmixins
|
正 |
コンポーネントオプションのmixins
|
P.194 5.5.1のオプションに関する記述
誤 |
methodsやcomponents,directives等の
|
正 |
methodsやcomponents、directives等の
|
P.194 5.5.1のシェアメソッドに関する記述
誤 |
コンポーネントで同名のシェアというメソッドを
|
正 |
コンポーネントで同名のshareというメソッドを
|
P.197 5.5.2の最後にJSONを文字列に変更する際のコードの誤り
誤 |
JSON.strinfigy({name: 'Evan You'})
|
正 |
JSON.stringify({name: 'Evan You'})
|
P.210,p.211 6.6.2の「複数の<style>ブロックの定義」における参照先
参照先が誤っていました。正しくは6.6.2以降で利用するコラムを参照します。
誤 |
先に利用した単一ファイルコンポーネントのFooコンポーネントとRootコンポーネントを以下のように変更します。
|
正 |
「コラム スコープ付きCSSのメリット(後述)」の単一ファイルコンポーネントfoo.vueとroot.vueを以下のように変更し、bar.vueをそのまま利用します。適宜サンプルを参照してください。
|
誤 |
先程と同じ画面がWebブラウザに
|
正 |
コラムと同じ画面がWebブラウザに
|
P.214 6.6.2の「子コンポーネントのルート要素におけるスタイルの注意事項」の表示例画像のキャプション
誤 |
socped属性によるカプセル化が
|
正 |
scoped属性によるカプセル化が
|
P.215 6章の「コラム スコープ付きCSSのメリット」のコード
コラム内のfoo.vue,root.vueのコードがtemplate部分のみ抜粋する形になっていたため全体を掲載いたします。
foo.vue
<template>
<div class="foo">
<h1 class="header">Fooコンポーネント</h1> <p>これはFooコンポーネントです。</p>
</div>
</template>
<style scoped>
.foo { border: solid 1px green; margin: 4px; padding: 4px; }
.header { font-size: 150%; }
</style>
|
root.vue
<template>
<div id="root">
<h1 class="header">Rootコンポーネント</h1> <p>これはRootコンポーネントです。</p>
<foo/> <bar/>
</div>
</template>
<script>
import Foo from './foo'
import Bar from './bar'
export default {
components: { Foo, Bar }
}
</script>
<style>
#root { border: solid 1px blue; margin: 4px; padding: 4px; }
.header { font-size: 200%; }
p { text-decoration: underline; }
</style>
|
P.219 6.6.3の$styleに関する記述
誤 |
Vue.jsのクラスに対して使用可能なのオブジェクト
|
正 |
Vue.jsのクラスに対して使用可能なオブジェクト
|
P.225 6.6.4のpug関連のパッケージをインストールする部分
pug-plain-loaderの追加が必要です。
誤 |
npm install --save-dev pug
|
正 |
npm install --save-dev pug pug-plain-loader
|
P.227,p.228 6章「コラム カスタムブロックの定義」内のVue Loader周りの最新版での動作
誤 |
$ npm install --save-dev deepmerge marked
|
正 |
$ npm install --save-dev marked
|
誤 |
const merge = require('deepmerge')
const loader = require.resolve('./loader.js') // カレントディレクトリに作成したカ
スタムローダーを読み込む
module.exports = {
chainWebpack: config => {
// Vue Loaderの設定をカスタマイズする
config.module
.rule('vue')
.use('vue-loader')
.tap(options =>
merge(options, {
loaders: {
// カスタムブロックをインポートしたカスタムローダーで処理する
docs: loader
}
})
)
.end()
}
}
|
正 |
const loader = require.resolve('./loader.js') // カレントディレクトリに作成したカスタムローダーを読み込む
module.exports = {
chainWebpack: config => {
// Vue Loaderの設定をカスタマイズする
config.module
.rule('docs')
.resourceQuery(/blockType=docs/)
.use('docs')
.loader(loader)
}
}
|
P.287 7.7.2の最後の説明
誤 |
ここで上げた2つの手法は
|
正 |
ここで挙げた2つの手法は
|
P.236 7.1の最後のリスト内のimport
誤 |
import TaskList from './TaskList'
|
正 |
import TaskList from './TaskList.vue'
|
P.251 7章の脚注*15
「この例では、incrementAsyncを使わずに書くと以下のようになります」の一文は必要ありません。
誤 |
この例では、incrementAsyncを使わずに書くと以下のようになります。この例では、incrementAsync...
|
正 |
この例では、incrementAsync...
|
P.278 7.6.1のストア登録に関する説明
誤 |
アクションがどのようにストアに登録にされるかを
|
正 |
アクションがどのようにストアに登録されるかを
|
P.286 7.7.2の説明文中の氏名の表記
誤 |
Danは初めから正しく分類...
|
正 |
Dan氏は初めから正しく分類...
|
補足情報
P.167 # 5.2.3のslot-scopeを使った例がVue 2.6.0以降で動かない例
v2.6からslotはroot elementが必須になり,v-ifを用いた本書のサンプルは動作しなくなっていました。v2.6以降で動作を試したい場合は下記のようにv-showに修正して試してください。
<!DOCTYPE html>
<title>Vue app</title>
<!-- 最新のvue読み込み -->
<div id="app">
<todo-list :todos="todos">
<li v-show="todo.isCompleted" slot-scope="{ todo }" :key="todo.id">
{{ todo.text }}
</li>
</todo-list>
</div>
|
var TodoList = {
props: {
todos: {
type: Array,
required: true
}
},
template: `
<ul>
<template v-for="todo in todos">
<!-- v-bindディレクティブでtodoを親コンポーネントに渡す -->
<slot :todo="todo">
<li :key="todo.id">
{{ todo.text }}
</li>
</slot>
</template>
</ul>
`
}
new Vue({
el: '#app',
data: function() {
return {
todos: [
{ id: 1, text: 'C++', isCompleted: true },
{ id: 2, text: 'JavaScript', isCompleted: false },
{ id: 3, text: 'Java', isCompleted: true },
{ id: 4, text: 'Perl', isCompleted: false }
]
}
},
components: {
TodoList: TodoList,
}
})
|
P.370〜P.372 # 10.3.1のloginアクションハンドラに関するテスト
一部のコードがVue.js本体の変更に伴い動作しなくなっていました。Vue 2.6 以降では,Vue.nextTick のタスクが下記のissue対応によって処理されるタイミングが変わってしまったため(MutationObserverによるmicroTask),イベントループでうまく処理されなくなってしまいました。
以下のように Vue.nextTick を使わずに, Promise.then ,Promise.catch で done をハンドリングすることで,アサーションできるように修正してください。diff形式で掲載しています。
- import Vue from 'vue'
import * as types from '@/store/mutation-types'
// loginアクション内の依存関係をモック化する
const mockLoginAction = login => {
// inject-loaderを使ってアクション内の依存関係をモック化するための注入関数を取得する
const actionsInjector = require('inject-loader!@/store/actions')
// 注入関数でAuth APIモジュールをモック化する
const actionsMocks = actionsInjector({
'../api': {
Auth: { login }
}
})
return actionsMocks.default.login
}
describe('loginアクション', () => {
const address = 'foo@domain.com'
const password = '12345678'
let commit
let future
describe('Auth.loginが成功', () => {
const token = '1234567890abcdef'
const userId = 1
beforeEach(done => {
const login = authInfo => Promise.resolve({ token, userId })
const action = mockLoginAction(login)
commit = sinon.spy()
// loginアクションの実行
future = action({ commit }, { address, password })
- Vue.nextTick(done)
+ future.then(() => done())
})
it('成功となること', () => {
// commitが呼ばれているかチェック
expect(commit.called).to.equal(true)
expect(commit.args[0][0]).to.equal(types.AUTH_LOGIN)
expect(commit.args[0][1].token).to.equal(token)
expect(commit.args[0][1].userId).to.equal(userId)
})
})
describe('Auth.loginが失敗', () => {
beforeEach(done => {
const login = authInfo => Promise.reject(new Error('login failed'))
const action = mockLoginAction(login)
commit = sinon.spy()
// loginアクションの実行
future = action({ commit })
- Vue.nextTick(done)
+ future.catch(() => done())
})
it('失敗となること', done => {
// commitが呼ばれていないかチェック
expect(commit.called).to.equal(false)
// エラーが投げられているかチェック
future.catch(err => {
expect(err.message).to.equal('login failed')
done()
})
})
})
})
|