Vue 入門
WARNING
コマンドやコードを示すときに{ユーザー名}
のような表記をすることがありますが、これは実際には自分の情報に合わせて{}
を取り除いた上で、自分のユーザー名を入力してください。
git clone
するときの例を挙げてみます。
$ git clone {リポジトリのURI}
上のように書いてあって traQ のリポジトリをクローンする場合、以下のように自分がクローンしたいリポジトリの URI を入力します。
$ git clone git@github.com:traPtitech/traQ.git
このような表記はしばしば出てくるので、{}
をつけたまま書いてしまわないように気をつけましょう。
Vue テンプレートのクローン
~/develop
ディレクトリの中にテンプレートリポジトリをクローンしてプログラムを書きます。
予め設定等が準備されたテンプレートリポジトリを用いて、最終的には TodoList を作っていきます。
GitHub に SSH 鍵を登録していない人は以下を参考にしてまず登録してください。
traPtitech/naro-template-frontend にアクセスし、「Use this template」→「Create a new repository」をクリックしてください。
「Repository name」にリポジトリ名を入力、公開状態は TA が見られるように「Public」にしてください。
「Create repository from template」でリポジトリを作成したら手元にクローンしてください。cd {リポジトリ名}
でプロジェクトのディレクトリに移動し、code .
で VSCode を開きます。
開いたプロジェクトの中に入っているpackage.json
というファイルには npm に関する様々な設定が書かれています。 この中には依存するパッケージ一覧も含まれており、以下のコマンドでそれらをインストールできます。
$ npm i
もしくは$ npm install
mehm8128@DESKTOP-6F4C0KI ~/develop/todolist-mehm8128 (main)$ npm i
added 130 packages, and audited 131 packages in 2s
20 packages are looking for funding
run `npm fund` for details
1 moderate severity vulnerability
To address all issues, run:
npm audit fix
Run `npm audit` for details.
テンプレートは初期状態でビルド&配信できるようになっているので、以下のコマンドを実行してブラウザで確認してみましょう。
$ npm run dev
mehm8128@DESKTOP-6F4C0KI ~/develop/todolist-mehm8128 (main)$ npm run dev
> todolist@0.0.0 dev
> vite
VITE v4.3.8 ready in 611 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
この状態で、ブラウザから localhost:5173 にアクセスすると、以下のような画面が表示されるはずです。
止めるときはCtrl + C
で止めてください。
Vue 入門
Vue とは
以下のリンクから公式ドキュメントに飛ぶことができます。
Vue
traP では、Web フロントフレームワークとして最も多く使われているフレームワークで、traQ、traPortal、Showcase、anke-to、knoQ などで使われています。
.vue
ファイルについて
Vue では.vue
という拡張子で単一ファイルコンポーネント(SFC, Single File Component)を作ることができます。
なろう講習会の言葉で言うと、Vue では、1 つの同じファイルに構造(HTML)・ロジック(JavaScript)・スタイル(CSS) を記述できます。それぞれを別の巨大なファイルに書くのではなく、見た目に対応した要素を各ファイルに分割して書くことで、それぞれの責任範囲をより直感的な形式で分けることができるわけです。このように分けられた要素をコンポーネントといいます。
Vue の書き方
<script>
タグ内にロジック<template>
タグ内に構造<style>
タグ内にスタイル
を記述します。
Sample.vue
<script>
// ロジック
</script>
<template>
<!-- 構造 -->
</template>
<style>
/* スタイル */
</style>
使用例
traQ で 1 つ例を挙げると、メッセージの表示部分はコンポーネントとして定義されています。 メッセージも複数のコンポーネントから構成されています。
- メッセージ情報(ユーザー名、画像、ファイル等)の構造(HTML)
- メッセージにスタンプをつける等のロジック(TypeScript)
- それらのスタイルを記述するスタイル(Scss)
が 1 つのファイルに纏められていることがわかります(このファイルはたった 130 行程度ですが、traQ にはコンポーネントが 300 個以上あります。 これがそれぞれの HTML、 CSS、 JavaScript のファイルに書かれていると想像してみると...)。
プロジェクト構造
.
├── index.html // 最初にブラウザに読まれるHTMLファイル
├── node_modules/ // 依存ライブラリの保存先
├── package-lock.json // 依存ライブラリ(詳細)
├── package.json // 依存ライブラリ・タスク・各種設定
├── public
│ └── favicon.ico // 静的ファイル(ビルドされない)
├── src
│ ├── App.vue // main.jsから読まれる.vueファイル(Vueの処理開始点)
│ ├── images // Vueで使用したい画像など
│ │ └── logo.svg
│ ├── components // 各種コンポーネント
│ │ └── HelloWorld.vue
│ └── main.ts // index.htmlから読まれるscript(TSの処理開始点)
└── vite.config.ts
index.html
Vite はここから参照されているファイルをたどってビルドを進めていきます。 ここに必要なものを書き加えることもありますが、基本的には書き換えません。 マウント用の<div id="app"></div>
とmain.js
の読み込みが書かれています。
node_modules
npm install
でインストールされる依存ライブラリが保存されるディレクトリです。 中を見ることは殆ど無いです。 .gitignore
に指定されています。(package.json
, package-lock.json
があれば npm install
で再現できるためです)
package.json
package-lock.json
プロジェクトに関するメタ情報や、npm run ~~
で実行できるタスク、依存ライブラリの情報が記述されています。
src/main.ts
index.html
で読み込まれている ts ファイルです。 ここでは Vue インスタンスを生成し、index.html
の<div id="app"></div>
部分にマウントしています。
src/App.vue
Vue としてのエントリーポイントです。 HelloWorld コンポーネントを読み込み → 登録 → 描画しています。
src/components/HelloWorld.vue
ここと似たようなものをどんどん書いていきます。 App.vue
で呼び出されています。components
内に他にも色々なコンポーネントがありますが、今回は使わないので省略します。
Vue を書く準備
まず、以下の拡張機能をインストールしてください。
Vue Dev tool
Chrome Devtool に Vue 向けのデバッグ機能を追加してくれます。
Vue devtools - Chrome ウェブストア
ソースコードの書き進め方
npm run dev
で起動していれば、ファイルの変更を自動で検知して表示が更新されます。
Vue を書く
Web 基礎講習会で書いたカウンターのソースコードを再掲します。
index.html(一部抜粋)
<body>
<h1>カウンター</h1>
<div id="count">回数: 0</div>
<button onclick="countUp()">クリック!</button>
<button onclick="reset()">リセット!</button>
</body>
counter.js
let count = 0
const countUp = () => {
count++
const countElement = document.querySelector('#count')
countElement.innerText = '回数: ' + count
}
const reset = () => {
count = 0
const countElement = document.querySelector('#count')
countElement.innerText = '回数: ' + count
}
INFO
Go や C++などでは""
と''
が区別されますが、JavaScript では区別されません。
Vue を書く
先にコードを書いてから解説を書いています。
意味がわからなくてもとりあえずコピー&ペースト or 写経しましょう。
ファイルの作成
components
ディレクトリ内にClickCounter.vue
というファイルを作成します。
ソースコードの変更
src/App.vue
style
タグを丸ごと消します。
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
src/components/HelloWorld.vue
script
タグ内でClickCounter.vue
を読み込み、template
タグ内にカウンターを配置します。
<script setup lang="ts">
import ClickCounter from './ClickCounter.vue'
defineProps<{
msg: string
}>()
</script>
<template>
<div>
<h1>{{ msg }}</h1>
<ClickCounter />
</div>
</template>
<style scoped>
a {
color: #42b983;
}
</style>
src/components/ClickCounter.vue
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref<number>(0)
const countMessage = computed(() => '回数: ' + count.value)
</script>
<template>
<div>
<div>{{ countMessage }}</div>
<button @click="count++">クリック!</button>
<button @click="count = 0">リセット!</button>
</div>
</template>
<style></style>
以下のように動けば OK です。
ソースコード解説
src/components/HelloWorld.vue
9~14 行目
テンプレート部分です。
Vue のコンポーネントは 1 つのタグの中に収まっている必要があります。
そのため、多くの場合 div タグで囲まれています。(ClickCounter.vue
も)
1 行目
<script setup lang="ts">
lang="ts"
に注目してください。実は今回は script タグ内では JavaScript なくて、TypeScirpt を書いています。TypeSciript は JavaScript に型がついたもので、SysAd のほぼ全てのプロジェクトで用いられています。型をつけることでバグを防ぐことができるので、この講習会でも TypeScript を使っていきます。
2 行目
import ClickCounter from "./ClickCounter.vue"
ClickCounter
コンポーネントを読み込む部分です。
4 行目
defineProps<{
msg: string
}>()
msg
props をstring
型で定義してる部分です。
今回だとApp.vue
で <HelloWorld msg="Hello Vue 3 + Vite" />
のような形でmsg
に値を指定することで、コンポーネントを使う側から値を渡しています。 JavaScript でいう関数の引数のようなものです。
参考: プロパティ | Vue
INFO
今回は template 内でしか props の値を使っていないのでmsg
で直接アクセスできていますが、script 内で使う場合は以下のようにしてprops
を定義し、props.msg
のようにアクセスする必要があります。
const props = defineProps()
また、このとき以下のように分割代入してしまうとリアクティビティ性が失われてしまうので、このような書き方はできません。
const { msg } = defineProps()
12 行目
<ClickCounter />
読み込んだコンポーネントを利用しています。
src/components/ClickCounter.vue
4 行目
コンポーネント内で利用する変数をこのように書きます。
ここではcount
という名前の変数をnumber
型で定義しています(実はこの程度なら TypeScript の型推論というものが効いて、初期値の0
から自動でcount
変数はnumber
型だと推論してくれます)。 ref
を使うことで、Vue が値の変更を検知して自動で再描画してくれるようになります。
const count = ref<number>(0)
INFO
ここでcounter.js
のcountUp
を見てみましょう。
const countUp = () => {
count++
const countElement = document.querySelector('#count')
countElement.innerText = '回数: ' + count
}
count
変数の値を変更した後に、DOM を直接操作して回数の値を更新しています。 Vue ではref
でcount
のような変数を定義するだけで、「値を更新」と「表示を変更」の 2 つをセットでやってくれるようになります。
11・12 行目
ボタンが押されたイベントに対する処理を書いています。@click
では、今回のように直接 JavaScript を記述するだけでなく、<script setup>
内で定義した関数の呼び出しもできます。
<button @click="count++">クリック!</button>
<button @click="count = 0">リセット!</button>
参考: イベントへの入門 - ウェブ開発を学ぶ | MDN
参考: イベントハンドリング | Vue
TIP
v-on:click
のショートハンドとして@click
という書き方ができます(推奨)
5 行目
computed
という機能を使って、表示するメッセージを生成します。count
の内容が変化すると自動的にcountMessage
の値が更新されるようになっています。
リアクティブな値にアクセスする時、<script setup>
の内部ではcount.value
のように、変数名のあとに.value
をつけてアクセスしてください。
const countMessage = computed(() => "回数: " + count.value)
参考: テンプレート構文 | Vue
参考: 算出プロパティ | Vue
今回のカウンターの全体像は以下のブランチに入っているので、参考にしてみてください。
traPtitech/naro-template-frontend at example/counter
Vue の嬉しさを実感する
いかがでしょうか?
この規模だと、ソースコードの行数としては生の HTML・JS で書いたほうが少ないですが、書く量を増やしてでも Vue を使う嬉しさがあります。
変数を操作するだけで表示が変わる
生 JS ではボタンのクリックごとに表示を変更する必要があります。
Vue ではコンポーネントの定義の時に表示と変数を関連付ける(5 行目・10 行目)ことで、変数を操作するだけで表示が自動で切り替わります。これが Vue の提供するリアクティビティです(コンポーネントが内部に保持している状態を変更したとき、その変更が Vue によって検知されて自動で HTML に反映されます)。
今回は、変数を操作する箇所が少ないのでまだ追えますが、この変数が様々なところで変更されるものだった場合を考えてみましょう。
生 HTML・JS ではその全ての場所で書き換えるコードを忘れることなく書かなければなりません。
複数のプログラマでコードを書いた場合、それを忘れないようにするということはかなりのコストになってしまうので辛いです。
Vue ではそれがなくて嬉しいです。
一度コンポーネントを登録すれば使い回せる
カウンターを 2 つ作りたくなった場合を考えます。
生 HTML・JS が書ける人はちょっとチャレンジしてみてください。関数名や変数名をかぶらないようにしたり、セレクタの名前を変更したりと結構めんどくさいです。
Vue ならば、HelloWorld.vue
の<ClickCounter />
をコピーして増やすだけで OK です。
これは traQ のように同じ要素を沢山利用するような Web アプリで大きな利点となります。