Vue.js 勉強記録

未経験からフロントエンドエンジニアに転職し、Vue.jsを勉強中なので、勉強したことの備忘録。
初投稿です。誤りやアドバイス等あればご指摘ください。


# Vue.js とは
JavaScriptのフレームワーク。
コンポーネントとして各プログラムを部品化し、再利用することができる。
Vue.js はDOM操作を自動で行ってくれるため、コードが複雑になりにくいのが利点。
コンポーネントはHTMLとJavaScriptをセットにできる（単一ファイルコンポーネントならCSSもセットにできる）。

# Vue.js でHello, World を表示させる
```index.html
<div id="app">
    <p>{{ message }}<p>
</div>
```

```main.js
var app = new Vue({
    el: "#app",
    data: {
        message: "Hello, World"
    }
```

# Vue.js のディレクティブ一覧
v- から始まるVue.js 独特の属性。

## v-bind
要素の属性値に、Vueインスタンスのデータを結びつける
`:`で省略表記できる。

```index.html
<div id="app">
    <a v-bind:href="url">google</a>
</div>
```

```main.js
var app = new Vue({
    el: "#app",
    data: {
        url: "https://www.google.com/"
    }
```

## v-on
クリックした時などのDOMイベントの受け取りを行う。
`@` で省略表記できる。

```index.html
<div id="app">
    <button v-on:click="greet">イベント発生</button>
</div>
```

```main.js
var app = new Vue({
    el: "#app",
    methods: {
        greet: function() {
            alert('Hello!');
    }
```


## v-if, v-show
条件分岐を記述する。

・v-if

```index.html
<div id="app">
    <div v-if="type === 'A'">A</div>
    <div v-else-if="type === 'B'">B</div>
    <div v-else>C</div>
</div>
```

```main.js
new Vue({
    el: "#app",
    data: {
	type: "B"  // Bが表示される
    }
})
```


・v-show

```index.html
<div id="app">
	<div v-show="type === 'A'">A</div>
	<div v-show="type === 'B'">B</div>
	<div v-show="type === 'C'">C</div>
</div>
```

```main.js
new Vue({
    el: "#app",
    data: {
        type: "C"  //Cが表示される
    }

})
```

v-ifによる条件を満たさなかった場合は要素はコメント化され、削除される。
v-showによる条件を満たさなかった場合は、要素に`display: none;` が付与される。


## v-for
dataオプションに登録された配列やオブジェクトから要素を繰り返して表示する。
下記では`animal`という変数に代入しているが、何でも良い。

```index.html
<div id="app">
	<ul>
		<li v-for="animal in animals" :key="animal.id">
			{{ animal }}
		</li>
	</ul>
</div>
```

```main.js
new Vue({
	el: "#app",
	data: {
		animals: ["cat", "dog", "bird", "monkey"]
	}

})
```

## v-model
双方向データバインディングを行う。フォームの入力値と同期させたいデータを同期させる。

```index.html
<div id="app">
	<input type="text" v-model="message">
	<p>{{ message }}</p>
</div>
```

```main.js
new Vue({
	el: "#app",
	data: {
		message: 'hogehoge'
	}

})
```


# オプションの構成
## computed（算出プロパティ）
`methods`と似ているが違いは下記の通り。
### methods
・キャッシュされない
・呼び出すときに`()`が必要
・再描画が起きるたびに更新される

### computed
・キャッシュされる
・呼び出すときに`()`が不要
・依存するVueインスタンスの値が更新された時のみ更新される


```index.html
	<div id="app">
		<div>
			縦：<input type="text" v-model="height">
			横：<input type="text" v-model="width">
		</div>
		<p>面積: {{ squareArea }}</p>
	</div>
```

```main.js
new Vue({
	el: "#app",
	data: {
		height: "",
		width: ""
	},
	computed: {
		squareArea: function () {
			return this.height * this.width
		}
	}
})
```

## watch
特定のデータまたは算出プロパティの状態を監視し、変化があったときに登録した処理を自動で実行する。

```index.html
<div id="app">
	<div class="form-group">
		名前：<input type="text" id="name" v-model="name">
		<p class="error" v-if="error.name">{{ error.name }}</p>
	</div>
</div>
```

```main.js
new Vue({
	el: '#app',
	data: {
		name: '',
		error: {}
	},
	watch: {
		name: function (value) {
			if (value === '') {
				this.error.name = '名前は入力してね'
			} else {
				this.error.name = ''
			}
		}
	}
})
```


# ライフサイクルフック
公式ドキュメントがわかりやすかった。
[ライフサイクルダイアグラム](https://jp.vuejs.org/v2/guide/instance.html#%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%83%80%E3%82%A4%E3%82%A2%E3%82%B0%E3%83%A9%E3%83%A0)


#コンポーネント
機能を持つ部品ごとに切り分けて、管理する仕組み。
部品ごとに管理することにより、再利用やカスタマイズが容易になる。
`methods`や`computed`などのオプションを持たせられる。

## グローバル登録
自動的に全てのコンポーネントから使用できる

```index.html
<div id="app">
	<button-hoge></button-hoge>
</div>
```

```main.js
Vue.component("button-hoge", {
	data: function () {
		return { count: 0 };
	},
	methods: {
		countUp: function () {
			this.count += 1;
		}
	},
	template: `
    <button @click="countUp()">
      {{ count }} 回押した
    </button>
  `
});

new Vue({
	el: '#app'
})
```

## ローカル登録
特定のコンポーネントに登録する。ローカルとして登録され、特定のコンポーネントのスコープ内でのみ使用できる。

```index.html
<div id="app">
	<btn-hoge></btn-hoge>
</div>
```

```main.js
var btnHoge = {
	template: `
	<button>hoge</button>
	`
};

new Vue({
	el: '#app',
	components: {
		'btn-hoge': btnHoge
	}
})
```

コンポーネントは、`new Vue`する前に定義する。
ルート要素は単一にすること。複数の要素があるときは`<div>`などで囲んであげる。

## 親子コンポーネント間のデータのやり取り

### 親→子
単方向なので、子から親へデータを渡すことはできない。
親が属性で渡して、子が`props`で受け取る

```parent.html
<!-- 親コンポーネント -->
<div id="app">
	<child hoge="hoge"></child>
	<child hoge="fuga"></child>
</div>
```

```child.js
// 子コンポーネント
Vue.component('child', {
	template: '<span>{{ hoge }}</span>',
	props: ['hoge']
})

new Vue({
	el: '#app'
})
```

・`props`は受け取りデータ型を指定することができる。
 以下、[公式ドキュメント](https://jp.vuejs.org/v2/guide/components-props.html#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%AE%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)より引用

```sample.js
Vue.component('my-component', {
  props: {
    // 基本的な型の検査 (`null` と `undefined` は全てのバリデーションにパスします)
    propA: Number,
    // 複数の型の許容
    propB: [String, Number],
    // 文字列型を必須で要求する
    propC: {
      type: String,
      required: true
    },
    // デフォルト値つきの数値型
    propD: {
      type: Number,
      default: 100
    },
    // デフォルト値つきのオブジェクト型
    propE: {
      type: Object,
      // オブジェクトもしくは配列のデフォルト値は
      // 必ずそれを生み出すための関数を返す必要があります。
      default: function () {
        return { message: 'hello' }
      }
    },
    // カスタマイズしたバリデーション関数
    propF: {
      validator: function (value) {
        // プロパティの値は、必ずいずれかの文字列でなければならない
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})
```

### 子→親
子が`$emit`で渡して、親が`on`で受け取る。

```index.html
<body>
	<div id="app">
	<child @childs-event="parentMethod"></child>
</div>
```

```main.js
// 子がイベントを起こす
Vue.component('child', {
	template: '<button @click="clickEvent">event!</button>',
	methods: {
		clickEvent: function () {
			this.$emit('childs-event')
		}
	}
})

// 親が受け取る
new Vue({
	el: '#app',
	methods: {
		parentMethod: function () {
			console.log("クリックされました");
		}
	}
})
```

## 終わりに
Vuex、Vue Router については勉強中。随時、Qiitaなどでアウトプットしながら、身につけていく。

# 参考資料

この記事は以下の情報を参考にして執筆しました。
https://jp.vuejs.org/
[基礎から学ぶVue.js](https://www.amazon.co.jp/dp/4863542453/ref=cm_sw_r_tw_dp_U_x_PbWqDb6FDT5S5 via @amazonJP)