【学習メモ】Vue.jsのチュートリアルやってみる（9~15章）

# 初めに
仕事でたまにVue.jsを使う機会があるのですが、よく分かっていなかったので一から学びました。
学んだことをアウトプットします。

# 使用したサイト
全15章。今回は9~15章。
https://ja.vuejs.org/tutorial/#step-1

# 学習メモ

## ライフサイクルとテンプレート参照
ref属性でテンプレートの要素を参照、操作することができるようになる
ref属性の付与された要素は`this.$refs.[値]`で参照できるようになる

コンポーネントがマウントされるまでrefにアクセスはできない
vueでは`mounted()`オプションを用いることで、マウント後のコンポーネントにアクセスできる

:::note info
`created()`と`mounted()`は両方ともライフサイクルフックだが、タイミングが異なる。
`created()`はDOM生成前。`mounted()`はDOM生成後。
:::

```vuejs
<script type="module">
import { createApp } from 'vue'

createApp({
  mounted() {
    this.$refs.pElementRef.textContent = "mounted!"
  }
}).mount('#app')
</script>

<div id="app">
  <p ref="pElementRef">Hello</p>
</div>
```

## ウォッチャー
プロパティの値が変更された時に実行する処理を定義できる

以下の例は、`todoId`の値が変わった時に`this.fetchData()`を行う処理をウォッチャーで定義している。
```vuejs
<script type="module">
import { createApp } from 'vue'

createApp({
  data() {
    return {
      todoId: 1,
      todoData: null
    }
  },
  watch: {
    todoId() {
      this.fetchData()
    }
  },
  methods: {
    async fetchData() {
      this.todoData = null
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${this.todoId}`
      )
      this.todoData = await res.json()
    }
  },
  mounted() {
    this.fetchData()
  }
}).mount('#app')
</script>

<div id="app">
  <p>Todo id: {{ todoId }}</p>
  <button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
  <p v-if="!todoData">Loading...</p>
  <pre v-else>{{ todoData }}</pre>
</div>
```

## コンポーネント
コンポーネントから別のコンポーネントを使用することができる（親と子）
親コンポーネントから利用するには
①子コンポーネントをインポートする
②インポートした子コンポーネントを`components`オプションに記載する
③`components`オプションに記載した子コンポーネントをケバブケースにしたタグをHTMLで使用する

```vuejs: 親コンポーネント
<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'
  
createApp({
  components: {
    ChildComp
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp></child-comp>
</div>
```

```js: 子コンポーネント
export default {
  template: `
  <h2>A Child Component!</h2>
  `
}
```


## props
子コンポーネント呼び出しで引数を渡すことができる
`props`オプションに引数を定義し、親コンポーネントから呼び出す際に`v-bind`で指定する


```vuejs: 親コンポーネント
<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      greeting: 'Hello from parent'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp v-bind:msg="greeting"></child-comp>
</div>
```

```js: 子コンポーネント
export default {
  props: {
    msg: String
  },
  template: `
  <h2>{{ msg || 'No props passed yet' }}</h2>
  `
}
```

## イベントの発行
子コンポーネントから親コンポーネントにデータを受け渡すためには、`emit`という機能を使用する
親コンポーネントでイベントの処理を定義しておき、子コンポーネントから呼びだす

親コンポーネントでは`<child-comp @response="(msg) => childMsg = msg"></child-comp>`という形でイベントを定義しておく
子コンポーネントでは
①`emits`オプションで使用するイベント名を宣言
②任意の箇所で`this.$emit('イベント名', 引数)`という形で親コンポーネントで用意したイベントを呼び出す

```vuejs: 親コンポーネント
<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      childMsg: 'No child msg yet'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp @response="(msg) => childMsg = msg"></child-comp>
  <p>{{ childMsg }}</p>
</div>
```

```js: 子コンポーネント
export default {
  emits: ['response'],
  watch: {
    value() {
      this.$emit('response', this.value)
    }
  },
  data() {
    return {
      value: ''
    }
  },
  template: `
  <h2 v-mode>Child component</h2>
  <input v-model="value"></input>
  <div>{{value}}</div>
  `
}
```

## スロット
`props`と同様、親コンポーネントから値を受け渡すことができる

子コンポーネントで`<slot></slot>`のように記載した個所について、親コンポーネントのスロットコンテンツをレンダリングすることができる

```vuejs: 親コンポーネント
<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      msg: 'from parent'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp>Test!!!</child-comp>
</div>
```

```js: 子コンポーネント
export default {
  template: `
  <slot>Default Value</slot>
  `
}
```

# 終わりに
vue.jsのチュートリアルはとても分かりやすく、ブラウザ上でできるので非常に手軽でした。
入門としては間違いないと思うので、これから始めてみたい型は是非試してみてください。

ここまでご覧いただきありがとうございました！