Vue社内勉強会に使った記事を晒す①

# 社内Vue勉強会①
__目的__
・Visual Studio Codeを使って、Vueの開発環境の構築ができる
・Vueの基本的な機能を理解する

__当日までの事前準備__
・Visual studio Codeをインストール済み
・ターミナル（Command line）の基本知識
・nodeのインストール（v16推奨）

__用語集__（後述します）
・**Vite**　Webpackに変わる高速ビルドツール
・**ディレクティブ** vueで使われるカスタム属性
・**コンポーネント開発** 画面（GUI）のモジュール化

__基本事項__
・typescriptを使用します（js,jxs,tsxも可能）
・cssを使用します（sass,stylusでもOK）
・VueはSingle File Components形式で説明します
・Vue3系で説明します
                

# 環境構築
便利なcliを使って、プロジェクトの雛型を作ります。
（↑ビルドツールにViteを使っています）

```sh
npm init vue@latest
```
パッケージのインストールが始まり、以下の質問に答えると雛型が出来上がります。
・単純化のため、テストライブラリと状態管理ライブラリ（Vuex）はNoとしています

```sh
> Project name: 
> Add TypeScript? -- Yes
> Add JSX Support？ -- No
> Add Vue Router for Single Page Application dev? -- Yes
> Add Pinia for state management? -- No
> Add Vitest for Unit testing? -- No
> Add Cypress for both Unit and E2E testing? -- No
> Add ESLint fro code quality? -- Yes
> Add Prettier for code formatting? -- Yes
```
これでプロジェクトは出来上がりです。次にサーバーを立ち上げてみます。
```sh
> cd プロジェクト 　＃プロジェクトに移動
> npm install  ＃依存関係をインポート
> npm run dev
```
http://localhost:3000
にアクセスすると、
![vue-init.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138727/a280f11e-bcf2-55a2-1752-7d8793e43456.png)
が表示されるはずです。

### Viteの説明（optional）
Vueの作者が作ったビルドツール。以下の特徴がある。
・typescript->javascriptへのトランスパイル（ただし型checkは無し）
・cssモジュールに対応（後述）
・ホットリロードに対応（コード変更箇所が画面に即時反映）
・esbuildを使っており、webpackよりもバンドルが早い

## プラグイン紹介
VSCodeで必要なpluginなどの説明をします。

#### Vue
- Volar
  Vue3のSnippetsの生成や、入力補完をしてくれる。
- ESLint
  Microsoftお手製のLintツール。
- Prettier（https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode）
  コードのフォーマットをしてくれるツール。

#### HTML&CSS
- Auto Closed Tag
  HTMLのタグを自動で閉じてくれる。
- Sass（Syler）
  Sassの補完やフォーマッターをしてくれる

#### Git（Optional）
- GitGraph gitコミットをグラフにしてくれる

### コード修正
__.vscode/settings.json__
コードを保存（ctrl+c）した時にフォーマットとeslint効かせることをオススメします。
```json
{
	"editor.codeActionsOnSave":{
		"source.fixAll.eslint":true //コード保存時にeslintを走らせる
	},
	"editor.formatOnSave":true, //formatter
	"editor.defaultFormatter": "esbenp.prettier-vscode" //プラグインのprettierを指定
}
```
を書いておくと、毎回保存した時にコード修正+フォーマットしてくれるので便利です。

__tsconfig.json__
tsc（typescriptコンパイラ）に食わせる設定ファイルです。
viteはtypescriptの、型チェックを有効にしておきます。
```json
{
	"compilerOptions":{
		"noEmit":true //これを追加
	}
}
```

# Vueの説明
## ファイル構造の説明
- .vscode/
- node_module/
- public/      	favicon位しか置かない？
- src/
	assets/     styleに関するファイルを置く。cssファイルや画像など。
	components/ モジュール単位のvueファイル
	views/		画面単位のvue
	App.vue		画面の親vue。全画面で共通な部分（ヘッダーなど）を記述。
- main.ts		index.htmlとApp.vueを結びつける。ルーターなどのモジュールをインポート。
- .eslintrc.cjs eslintに関するルールを記述
- .gitignore
- index.html	`<!DOCTYPE html>`が書かれた画面のエントリーポイント。
- package.json	npm installしたライブラリが書かれている
- tsconfig.json tscの設定ファイル
- vite.config.ts viteの設定ファイル

画面実装でいじるのは、`src/components/`と`src/views/`です。
次にvueのファイルを見てみます。

## Vueファイルの構造
App.vueを見てみると、`<script>`と`<template>`と`<style>`のタグがあります。
`<template>`にはHTML、`<style>`にはcssを書きます。

＊import文には`@`がありますが、`src/`のエイリアスです（tsconfig.jsonに書いてあります）

```vue
<script setup lang="ts"> //これはtypescriptで書くよという命令
import HelloWorld from "@/components/HelloWorld.vue"　// HellowWorldモジュールをインポート。
import TheWelcome from "@/components/TheWelcome.vue" // TheWelcomeモジュールをインポート
</script>

<template> // lang="pug"でpugテンプレートエンジンも選べる
...
	<div class="wrapper">
		<HelloWorld msg="you did it!"/>  //インポートしたモジュールを参照
	</div>
	...
	
	<main>
		<TheWelcome/> //インポートしたモジュールを参照
	</main>
	...
</template>

<style> // cssを記述する。
		// lang=sassでsassが書ける npm install sass sass-loaderが必要
...
</style>
```

このように、一つの画面に対して、スタイル（css）、構造（html)、アクション(ts/js)を同じ場所に書くのを、Single-File Component（SFC）と呼びます。

#### cssとモジュール化
画面共通のスタイルを指定する場合は、`src/assets/`以下にcssファイルを置きます。
`src/assets/`に置くファイルは、基本以下のようなものです。
- normalize.cssやreset.css
- 基本色やthemeなどが書かれたcss
- ボタンやアイコンなど画面共通のレイアウト（.buttonや.iconなど）

逆に、コンポーネントに書いたcssは、他のコンポーネントに影響を及ぼさない方が良いでしょう。
その場合は、`<style scoped>`と書けば、他のcssを上書きすることはありません。（cssのmodule化）

## Vueディレクティブの説明
ディレクティブは、「コンパイラーに与える補足情報」と定義されてます。
vueにも良く使うディレクティブがいくつかあるので紹介します。

#### v-class（または:class）
クラスを動的に付けたい場合は、`v-class`を使用します。
例えば、ボタンをactiveにしたい場合

__`演習`__ button要素を追加し、アクティブにする

#### v-on（または@）
Html要素のイベントハンドリングに使います。
良く使うのは、ボタンのクリックイベントですが、changeやblur,focusなど様々なイベントに対応しています。
`<button v-on:click=function($event)></button>`

__`演習`__ button要素のクリックイベントを取得する。


#### v-for
同じオブジェクトを複数回表示する場合に使います。
たとえば、動的なリストを表示する場合、`<li v-for="item in items">{{item}}</li>`のように使います。

__`演習`__ 人名を動的なリストで表示させる


#### v-model
inputやtextareaなどに入力された値を受け取ります。（そのままだとコンポーネントには使えません）
input要素に入力された値を、msgとして出力しましょう。

__`演習`__ input要素を追加し、それを表示する（refを使う）


これまで
ディレクティブは他にも、
>
- v-show
- v-if v-else
- v-style

などありますが、使い方は変わりません。


## Vueコンポーネント間のデータの渡し方
コンポーネントにデータを渡すには、propsとemitの方法があります。

#### props（親コンポーネント->子コンポーネント）
コンポーネントに渡す引数です。例えば、`<HelloWorld msg='You did it!'/>`の`msg`がpropsです。
'You did it!'のメッセージを変えると、表示も変わりますが、これはHelloWorld.vueに
`{msg:'You did it!'}`という形で引数が与えられ、それを表示しているからです。

引数を受け取るには`defineProps`を使います。
HelloWorld.vue実装を見てみると
```vue
<script setup lang="ts">
defineProps<{
	  msg: string
	}>()
</script>

<template>
...
	<h1 class="green">{{msg}}</h1> //ここで表示される。{{}}を使う
</template>

```
と、確かにmsgが受け取られているのが分かります。

__次に、HTMLに直接文字を書くのではなく、`msg`を変数として渡してみます。__
この場合は、文字列ではなく変数を受け取るために、`<HelloWorld :msg="msg">`となります。

__`演習`__ msgを変数にする。

#### emit（子コンポーネント->親コンポーネント）
子コンポーネントから親コンポーネントにデータを渡したい場合があります。
たとえば、ログインフォームの子コンポーネントである`InputPassword.vue`から親コンポーネント`Login.vue`に入力データを渡したい場合です。パスワードが入力されていない場合は、ログインボタンを押せないようにします。

こういう場合には、`emit`を使います。

__Login.vue__
```vue
<template>
	<InputPassword @update:password="update"/> //ここで受け取る
	<button :class={'is-active':active}>ログイン</button>
</template>

<script lang="ts" setup>
import {ref} from "vue"
const active = ref<boolean>(false)

const update = (value:string)=>{
	active.value = value.length!=0
}

</setup>
```

__InputPassword.vue__
```vue
<template>
	...
	<input v-model="password" type="password">
</template>
<script>
improt {watch} from "vue"

const password = ref<string>("")
const emits = defineEmits(['update:password'])

watch(password,()=>{　//passwordの値が変化した場合に、callbackが呼ばれる。
	emits('update:password',password.value) //password.valueの値が親コンポーネントに渡る
})

</script>

```

## 演習
ボタンを押したら+1ずつカウント表示されるカウンターを作ってみましょう。
ただし、ボタンは子コンポーネントとして切り出してください。