今回はVueの基本的機能を触りながら、どういった事ができるのかを把握していきましょう。
ゴール
- テンプレートのファイル構成を理解する
- Vueの基本的な機能を理解する
- Single File Componentでの実装を経験する
- Gitでローカルリポジトリにコミットする
準備
ディレクトリの構成とコンポーネントの準備
前回作成したプロジェクトをVisual Studio Code(VSCode)で開きます。
VSCを開いて「フォルダーを開く」でも良いですし、
エクスプローラーから右クリックで「Codeで開く」でもOKです。
開いたらCtrl+Shift+Eか、画面左端のExplorerタブをクリックしてファイル構成を確認しましょう。
|-- .vscode プロジェクトで共有するVSCodeの設定ファイルが格納される
|-- node_modules npmでインストールしたモジュールが格納される(基本いじらない)
|-- public ソースコードで参照されないファイルを置く特別なディレクトリ
|-- src Vueアプリのソースコード
| |-- assets ソースコードから参照する静的ファイルを置く場所(画像やCSSなど)
| |-- components Vueのコンポーネント格納ディレクトリ
| |-- App.vue Vueアプリのトップレベルコンポーネント
| `-- main.js Vueアプリの起動場所
|-- .gitignore Gitで管理しない無視するディレクトリ/ファイルを記載するファイル
|-- index.html アプリケーションのエントリポイントであるHTMLファイル
|-- package-lock.json npm install時のパッケージ情報(バージョン等)を保持するファイル
|-- package.json npmコマンドや依存するnpmモジュールを管理するファイル
|-- README.md 読んでね
`-- vite.config.js Vite用のコンフィグファイル
たくさんディレクトリなどがあるので混乱するかも知れませんが、
開発の時にソースコードを実装していくのはsrc
ディレクトリで他はあまり触りません。
今回もsrc
ディレクトリの中のファイルを編集・追加する形で実装していきます。
コンポーネントの変更による影響をすぐ確認できるようにするために、
npm run dev
コマンドで開発環境を立ち上げ、http://localhost:3000/を開いておいてください。
Git BashのターミナルはVSCode上で起動するのがオススメです。
今は、App.vue
とcomponentsディレクトリ内のHelloWorld.vue
が使われてますが、
HelloWorld.vue
は削除し、App.vue
は一旦まっさらにした状態で作成していきます。
まずnpm run dev
が実行されている状態のままHelloWorld.vue
ファイルを削除してください。
すると、アプリケーション側でもターミナル側でも同じエラーが表示されるはずです。
こういった形で、ViteはVueのコンポーネントの依存のエラーなどはメッセージで表示してくれます。
エラーが起きた時は落ち着いて文章を読んで対処してみてください。
今回は削除したHelloWorld.vue
のインポートが解決しないというエラーなので、
App.vue
側のインポートしている部分を削除することでエラーを解決します。
App.vue
のコードを全て消して、以下のように修正して保存してみてください。
<template>
<h1>My Vue Playground</h1>
</template>
エラーが消えて、とてもシンプルな画面が表示されます。うまくいかない場合はリロードしてみてください。
Single File ComponentはHTML/CSS/JavaScriptの全てを含むと説明しましたが、
実はミニマルな状態ではHTMLを表す<template>
だけでも実装可能です。
JavaScriptの<script>
とCSSの<style>
は必要に応じて実装します。
Gitの準備
実際の開発では共有のリモートリポジトリ(GitLabやGitHub)からローカルリポジトリをクローンして利用します。
ですが、今回は自分でローカルリポジトリを作ってコミットしていく流れも合わせて体験していきましょう。
まずはgit init
コマンドで、ローカルリポジトリを作成します。
$ git init
Initialized empty Git repository in C:/dev/vite-vue-project/.git/
そうすると隠しフォルダの/.git
ディレクトリが作成されます(VSCode上からはデフォルトで見えません)。
同時にVSCodeのExplorerエリアのファイルの大多数が以下のような表示になり、
Source Controlに数字付きのバッジが表示されます。
緑で「U」がついているファイルはUntracked FileといってGitで管理されていないファイルやディレクトリを示します。
node_modules
ディレクトリがグレーなのは、.gitignore
内でnode_modules
を無視するよう定義しているためです。
.gitignore
内のファイルはGitによる管理の対象外になります。
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
(以下略)
左のSource Controlボタンを押すと、ExplorerがSource Controlに切り替わります。
ここでは、変更があったファイルが全て差分とともに表示されます。
今回は初期コミットなので、全てのファイルが新規作成(Untracked)状態です。
この状態で、Changes横のStage All Changesのプラスマークを押します。
すると、ファイル横の「U」が「A」に変わります。これはAddedのAで、ステージングされたことを示します。
このステージングの作業は、今のようにまとめて行うこともできれば、ファイル一個単位でも可能で、
その場合はファイル名のすぐよこにカーソルを合わせると表示されるプラスマークを押します。
コミットしたいファイルがすべてステージされていることを確認したら、
Message欄に任意のメッセージを入力して、チェックマークを押してコミットしてください。
これで今の状態がローカルリポジトリにコミットされました。
試しにApp.vueの文字に変更を加え保存してみると、Modifiedの「M」の文字がでます。
リモートブランチにプッシュする際は今の一連の流れを新たなブランチで実施して、
マージリクエストを投げるようにしてください。
保存時のフォーマットの有効化
Ctrl+Shift+pで出てきたフィールドにWorkspaceと打ち込みWorkspace Settingsを表示してください。
するとワークスペースの設定画面がでるので、formatと入力しFormat On Saveのチェックをオンにします。
そうするとディレクトリ内の.vscode/settings.json
というファイルが生成されます。
これはワークスペース単位の設定なので共同開発時には他の開発者と共有すべきファイルです。
ですが、このテンプレートだと.gitignore
の対象になっているので、
.gitignore
に!.vscode/settings.json
を追記してください。
このように.gitignore
は先頭に!
をつけることで特定のファイルを無視する対象から除外可能です。
extensions.json
が.vscode/*
配下でもあるに関わらずコミットされているのはそのためです。
Vueの基本機能
宣言的レンダリング Declarative Rendering
ここからはApp.vue
でいろいろな実装を試していきます。
まずは、JavaScriptのデータをTemplate上で表示する宣言的レンダリングです。
{{ }}
を使って、Vueのコンポーネントのdata
の値を表示します。
まずは、ヘッダータグに任意のデータを表示してみましょう。
<script>
export default {
data() {
return {
title: 'My New Vue Title'
}
}
}
</script>
<template>
<h1>{{ title }}</h1>
</template>
これで、画面上のタイトルが<script>
のdata
内に記述した値に変更されました。
VueのOptions APIではexport default
でエクスポートしているオブジェクトの、
様々なオプションにデータやメソッドなどを実装していき、それをテンプレートと紐づけ動的なアプリを実現します。
このdata
内の値はリアクティブであり、値が更新されると表示も同時に更新されます。
v-bind 属性バインディング
HTML内のテキストだけでなく、要素の属性とも文字列をバインディング(結びつける)ことが可能です。
data
にmessage
を追加して、テンプレート側でh1
要素にtitle
属性を追加してみましょう。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue'
}
}
}
</script>
<template>
<h1 v-bind:title="message">{{ title }}</h1>
</template>
これを保存すると画面上で変化は見えませんが、
ヘッダーにマウスオーバーするとmessage
の値が表示されるはずです。
このv-bindは他にもあらゆる属性と結びつけが可能です。
よく使うclass
とのバインディングを試してみましょう。
通常クラスを指定する時はclass="red"
のように実装します。
Vueで動的にクラスの割り当てを管理する時は、オブジェクトを用意し、
クラス名に対しbooleanの値でクラスを当てるか当てないかの切り替えが可能です。
ヘッダータグの色を変えるためにクラスを当てる以下のコードを試してみてください。
<script>
export default {
data() {
return {
title: "My New Vue Title",
message: "Welcome to Vue",
isRed: true,
};
},
};
</script>
<template>
<h1 v-bind:title="message" v-bind:class="{ red: isRed }">{{ title }}</h1>
</template>
<style>
.red {
color: red;
}
</style>
スクリプト内のisRed
がtrue
なので、テンプレート内の:class
にてred
クラスがあたり、
新たに追加した<style>
内のクラスが適用され文字色が赤くなります。
試しにisRed
をfalse
にしてみてどういう変化をするか確認してみてください。
このオブジェクトはテンプレート側で定義してもOKですし、スクリプト側で定義するでもOKです。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
textStatus: {
red: true,
},
};
},
};
</script>
<template>
<h1 v-bind:title="message" v-bind:class="textStatus">{{ title }}</h1>
</template>
<style>
.red {
color: red;
}
</style>
状況によって使い分ける必要があります。
適用するクラスが細かく分かれている場合はスクリプト、単体ならテンプレートにするのがオススメです。
あるいは、すべてどちらかに統一するでも良いと思います。その場合は責務の関係上テンプレート側がオススメです。
今回は一旦テンプレート側で記載する方針で進めます。isRed
を使っていたコードに戻してください。
v-bindはVueで開発する際に多用します。そのため略記法が用意されているので、使ってみましょう。
v-bind:xxx
のv-bind
を削除して:xxx
と先頭にコロンが付く形でもv-bind
とみなされます。
テンプレートを以下のように修正してください。
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
</template>
同様に機能していることを確認してみてください。
このようなv-
から始まるVueが用意する属性をディレクティブと言います。
このディレクティブや{{ }}
で指定する値は単一のJavaScriptの式としてみなされます。
なので直接式を書き込むような以下のようなコードでも動作します。
<template>
<h1 :title="123 * 3" :class="{ red: !true }">{{ 'ようこそ' + 'Vueへ' }}</h1>
</template>
動作を確認したらコードを戻しておいてください。
computed 算出プロパティ
テンプレート内に式を書けることを紹介しましたが、
多くの場合テンプレート内の記述が冗長になり肥大化しメンテナンス性が下がります。
また、同じ値を何回も利用したい場合、同じ式をリピートして書かなくてはいけません。
以下で言えば、フルネームを表示するために同じ式を2度書いています。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
user: {
firstName: 'John',
lastName: 'Smith',
},
};
},
};
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<h2>{{ user.firstName + " " + user.lastName }}さんのデータ</h2>
<p>Name: {{ user.firstName + " " + user.lastName }}</p>
</template>
そういった場合のためにVueではcomputed
オプション(算出プロパティ)が用意されています。
computed
を利用してフルネームを返すプロパティを作成してみましょう。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
user: {
firstName: 'John',
lastName: 'Smith',
},
}
},
computed: {
fullName() {
return this.user.firstName + ' ' + this.user.lastName
},
},
}
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<h2>{{ fullName }}さんのデータ</h2>
<p>Name: {{ fullName }}</p>
</template>
computed
はdataの値に対してリアクティブに更新されます。
fullName
でuser
オブジェクトを利用する際に、先頭にthis.
がついていることを留意してください。
これは、Vueインスタンス内で利用するdata
,computed
,methods
などのオプションの各プロパティは、
this.
を先頭に書いてこのVueインスタンスのプロパティであることを指定しなくてはいけません。
テンプレート側ではthis.
は不要なので、混乱しないように注意してください。
後に紹介するメソッドを指定することで似たことは実現できますが、
computedの値はキャッシュがなされパフォーマンスの最適化がなされます。
可能な限りcomputedを使うようにしましょう。
v-if & v-else 条件付きレンダリング
テンプレート内のブロックを条件に応じて描画したい時にv-if
ディレクティブを利用します。
v-if
はスクリプト内のbooleanな値に対してリアクティブに描画が切り替わります。
ユーザーデータにメンバーであるかのisMember
をもたせ、それに応じて表示を切り替えてみます。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
user: {
firstName: 'John',
lastName: 'Smith',
isMember: true,
},
}
},
computed: {
fullName() {
return this.user.firstName + ' ' + this.user.lastName
},
},
}
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<h2>{{ fullName }}さんのデータ</h2>
<p>Name: {{ fullName }}</p>
<p v-if="user.isMember">メンバーです</p>
</template>
isMember
がtrue
の場合、テンプレートの<p v-if="user.isMember">メンバーです</p>
が表示されます。
isMember
をfalse
に変えて表示が消えることを確かめてみてください。
続いて、v-if
とセットで使うv-if
に該当しない場合に表示するためのv-else
ディレクティブを利用します。
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<h2>{{ fullName }}さんのデータ</h2>
<p>Name: {{ fullName }}</p>
<p v-if="user.isMember">メンバーです</p>
<p v-else>メンバーではありません</p>
</template>
v-else
ディレクティブを追加した状態でisMember
のtrue/false
を切り替えて表示の確認をしてください。
v-if
及びv-else
は指定した子要素まで影響が及びます。
v-for リストレンダリング
リストのデータを元に同じ要素を繰り返し描画したいケースは多くあると思います。
たとえば、複数のユーザーや複数の日付のデータを表示するなどのケースです。
そういったケースでは、v-for
ディレクティブを利用します。
今回はユーザーデータをリスト(配列)にして複数ユーザーの表示を試します。
大きくコードを変えるので注意してください。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
users: [
{
firstName: 'John',
lastName: 'Smith',
isMember: true,
},
{
firstName: 'Taro',
lastName: 'Shinjuku',
isMember: false,
},
{
firstName: 'Hanako',
lastName: 'Shibuya',
isMember: true,
},
],
}
},
}
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<h2>ユーザーのデータ</h2>
<div v-for="user in users">
<p>Name: {{ user.firstName + ' ' + user.lastName }}</p>
<p v-if="user.isMember">メンバーです</p>
<p v-else>メンバーではありません</p>
</div>
</template>
data
のuser
をusers
と名前を変えて配列データに変え、
それをテンプレート内でdiv
要素にv-for
ディレクティブを追加し繰り返し描画しています。
v-for="user in user"
のusers
がdata
のusers
を指定しており、
その配列の要素1つ1つをuser
としてv-for
内では利用可能になっています。
v-for
は入れ子になって複数の階層でも利用可能です。
v-on & methods イベントハンドリング
ここまではコンポーネントの値を元にテンプレート側で表示をしてきました。
ここからは、テンプレート側で発生したイベントを元に特定の処理を実施するための実装を試します。
v-on
ディレクティブでDOMイベントを拾い、イベント発火時にJavaScriptのコードを実行できます。
実行するコードはシンプルなものならテンプレート内に書くこともできますが、
基本的にはOptions APIのmethods
プロパティにメソッドとして定義します。
今回はデータは固定ですがユーザーをリストに追加する処理とそれを実行するボタンを作ります。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
users: [
{
firstName: 'John',
lastName: 'Smith',
isMember: true,
},
{
firstName: 'Taro',
lastName: 'Shinjuku',
isMember: false,
},
{
firstName: 'Hanako',
lastName: 'Shibuya',
isMember: true,
},
],
}
},
methods: {
addUser() {
this.users.push({
firstName: 'First',
lastName: 'Last',
isMember: true,
})
},
},
}
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<button v-on:click="addUser">ユーザー追加</button>
<h2>ユーザーのデータ</h2>
<div v-for="user in users">
<p>Name: {{ user.firstName + ' ' + user.lastName }}</p>
<p v-if="user.isMember">メンバーです</p>
<p v-else>メンバーではありません</p>
</div>
</template>
button
要素にv-on:click="addUser"
というディレクティブが実装されています。
これはボタン要素のクリックイベントを拾うとaddUser
を実行するという定義になってます。
addUser
はスクリプト内のmethods
に定義された関数で、
data
に定義されているusers
に固定のユーザーデータをpush
で追加しています。
v-on
ディレクティブはかなり多用するので、これも略記方が用意されています。
v-on:click
を@click
と書くことが可能です。基本的にこちらを利用しましょう。
以下のように修正して同じように動くか試してみてください。
<button @click="addUser">ユーザー追加</button>
v-on
はclick
イベントだけでなくblur
やkeydown
など基本的なイベントが利用可能です。
onclick
から@click
、onfocus
から@focus
のようにすることで利用したいイベントに応じて、
ハンドラーを設定して実装をしていきましょう。
v-model フォームバインディング
フォームの値をVueのアプリケーションでハンドリングしたいケースは多々あります。
これまで試してきたv-bind
やv-on
でそれらを実装することも可能ですが、
Vueではv-model
ディレクティブが用意されておりこれにより簡単にフォームとデータのバインディングが可能です。
先程は固定の値を追加していたユーザー追加ボタンですが、これを任意の値を入力できるようにしましょう。
<script>
export default {
data() {
return {
title: 'My New Vue Title',
message: 'Welcome to Vue',
isRed: true,
input: {
firstName: '',
lastName: '',
isMember: true,
},
users: [
{
firstName: 'John',
lastName: 'Smith',
isMember: true,
},
{
firstName: 'Taro',
lastName: 'Shinjuku',
isMember: false,
},
{
firstName: 'Hanako',
lastName: 'Shibuya',
isMember: true,
},
],
}
},
methods: {
addUser() {
this.users.push(this.input)
this.input = {
firstName: '',
lastName: '',
isMember: true,
}
},
},
}
</script>
<template>
<h1 :title="message" :class="{ red: isRed }">{{ title }}</h1>
<input type="text" v-model="input.firstName" />
<input type="text" v-model="input.lastName" />
<input type="checkbox" v-model="input.isMember" />
<button v-on:click="addUser">ユーザー追加</button>
<h2>ユーザーのデータ</h2>
<div v-for="user in users">
<p>Name: {{ user.firstName + ' ' + user.lastName }}</p>
<p v-if="user.isMember">メンバーです</p>
<p v-else>メンバーではありません</p>
</div>
</template>
これで入力した値に紐づく形でユーザー情報が追加できるようになりました。
addUser
メソッド内ではusers
にプッシュする値をinput
にし、
それを追加後input
の内容をリセットすることで同じデータを追加しないようにしています。
現状では名前が空でも追加できるので、もし余裕がある方はバリデーション機能を実装してみてください。
今回はinput
タグのtext
とcheckbox
を利用していますが、異なるタイプにも対応しています。
他の使い方については公式ドキュメントを確認してください。
Gitのコミット
さいごに修正したApp.vueをGitのローカルリポジトリにコミットして終了です。
まとめ
今回はVueの以下の基本的な機能を実装しながら学習しました。
-
{{ }}
テンプレート構文 -
computed
算出プロパティ -
v-bind
によるバインディング -
v-if
とv-else
による条件付きレンダリング -
v-for
によるリストレンダリング -
v-on
とmethods
イベントハンドリング -
v-model
によるフォームバインディング
以上の基本的な機能をマスターすることで、
Vueでは動的なアプリの実装が可能になります。
次回のチュートリアルではここで学んだ内容をベースにToDoアプリを作成していきます。