【Vue.js】コンポーネントについて

## Vueファイルについて
拡張子が.vueとなっているファイルで、基本的にこのvueファイル一つで一つのコンポーネントを構成する。

```App.Vue
<template>
<!-- ここにhtmlを記載する -->
<h1 v-bind:class="{red:isRed}">{{ title }}</h1>
</template>


<script>
/*ここにjavascriptを記載する*/
export default {
  data() {
    return {
      title: 'Hello! Vue.js',
      isRed: true,
    }
  }
}
</script>


<style>
/* ここにcssを記載する */
.red{
  color: red;
}
</style>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673845/380a92c9-8d17-e078-bd8c-8b7e22406b22.png)


## ToDoアプリを作成してみる

```App.Vue
<template>
   <div class="addli">
      <h1>ToDoアプリ</h1>
      <input type="text" v-model="newTodoText" placeholder="To Do">
      <button @click="addTodo">追加</button>
      <button @click="clearDoneTodos">完了済みを削除</button>
   </div>
   <div id="todo-app">
     <p v-if="todos.length===0">Todoがありません！</p>
     <ul v-else>
       <li v-for=" todo in todos">
         <input type="checkbox" v-model="todo.isDone"/><span :class="{'todo-done':todo.isDone}">{{ todo.text }}</span>  
      </li>
     </ul>
   </div>
</template>


<script>
export default {
  data() {
    return {
      newTodoText:'',
      todos: [],
    }
  },
  methods: {
    addTodo() {
      if(!this.newTodoText) return alert('Todoを入力してください！')
      this.todos.push({
        isDone: false,
        text:this.newTodoText,
      })
      this.newTodoText=''
    },
    clearDoneTodos() {
      this.todos = this.todos.filter((todo)=>!todo.isDone)  
    },
  },
}
</script>


<style>

button{
  margin: 5px;
}

.addli{
  display: block;
}

#todo-app{
  display: flex;
　flex-direction: column;
}
}

.todo-done {
  text-decoration: line-through;
}

</style>

```


### 再利用性や可読性を高める為にコンポーネントに分割してみる
ToDoを追加するためのフォームをcomponents/TodoAdd.vueに切り出してみる。

## $emit使い方(子から親へ)
子→親に値を渡す時には$emitを使う。




```TodoAdd.Vue
<template>
   <div class="addli">
      <h1>ToDoアプリ</h1>
      <input type="text" v-model="newTodoText" placeholder="To Do">
      <button @click="todoAdd">追加</button>
      <!-- /2.$emitでカスタムイベントをつくる -->
      <button @click="$emit('delete-done')">完了済みを削除</button>
   </div>
</template>


<script>
export default {
  /*1.emitsオプションで発火するイベント名を定義する*/
  emits:['delete-done','add-todo'],
  data() {
    return {
      newTodoText: '',
    }
  },
  methods: {
    todoAdd() {
      /*2.$emitでカスタムイベントをつくる
        第２引数に送信するデータを渡す
      */
      this.$emit('add-todo', this.newTodoText)
      this.newTodoText = ''
    },
  },
}
</script>




```

```App.Vue
<template>
   <div id="todo-app">
    <!-- /3.作成したカスタムイベントを付与する -->
    <TodoAdd @delete-done="clearDoneTodos" @add-todo="addTodo"/>
     <p v-if="todos.length===0">Todoがありません！</p>
     <ul v-else>
       <li v-for=" todo in todos">
         <input type="checkbox" v-model="todo.isDone"/><span :class="{'todo-done':todo.isDone}">{{ todo.text }}</span>  
       </li>
     </ul>
   </div>
</template>


<script>
import TodoAdd from './components/TodoAdd.vue'


export default {
  components: {
    TodoAdd,
  },
  data() {
    return {
      todos: [],
    }
  },
  methods: {
    addTodo(newTodoText) {
      if(!newTodoText) return alert('Todoを入力してください！')
      this.todos.push({
        isDone: false,
        text: newTodoText,
      })
    },
    clearDoneTodos() {
      this.todos = this.todos.filter((todo)=>!todo.isDone)  
    },
  },
}
</script>


```
## props使い方(子から親へ)
親→子に値を渡す時にはpropを使う。
Todoリストのリスト部分をcomponents/TodoList.vueに切り出してみる。


```TodoList.Vue

<template>
     <ul>
       <li v-for=" todo in todos">
         <input type="checkbox" v-model="todo.isDone"/><span :class="{'todo-done':todo.isDone}">{{ todo.text }}</span>  
       </li>
     </ul>
</template>

<script>
export default {
    /*1.propsという属性を定義し、propの名前と型を定義する*/
    props: {
        todos:Array,
    },
}
</script>
```
```App.Vue

<template>
   <div id="todo-app">
    <TodoAdd @delete-done="clearDoneTodos" @add-todo="addTodo"/>
     <p v-if="todos.length===0">Todoがありません！</p>
    <!-- /2. prop名と値を受け渡す-->
    <TodoList  v-else :todos="todos"/>
   </div>
</template>


<script>
import TodoAdd from './components/TodoAdd.vue'
import TodoList from './components/TodoList.vue'

export default {
  components: {
    TodoAdd,
    TodoList,
  },
  data() {
    return {
      todos: [],
    }
  },
  methods: {
    addTodo(newTodoText) {
      if(!newTodoText) return alert('Todoを入力してください！')
      this.todos.push({
        isDone: false,
        text: newTodoText,
      })
    },
    clearDoneTodos() {
      this.todos = this.todos.filter((todo)=>!todo.isDone)  
    },
  },
}
</script>


<style>

button{
  margin: 5px;
}

.addli{
  display: block;
}

#todo-app{
  display: flex;
　flex-direction: column;
}

.todo-done {
  text-decoration: line-through;
}


</style>


```


## slotの使い方
TodoAdd.vueのボタンをMyButton.vueというコンポーネントに切り出してみる。

```MyButton.Vue
<template>
  <button class="my-btn">
   <!-- /1.子コンポーネントでslotを定義する -->
　<slot />
  </button>
</template>

<style>
.my-btn {
  color: #fff;
  background-color: #333;
  margin-left: 0.5em;
  border-radius: 0.5em;
}
</style>
```

```TodoAdd.Vue
<template>
   <div class="addli">
      <h1>ToDoアプリ</h1>
      <input type="text" v-model="newTodoText" placeholder="To Do">
      <!-- /2.親コンポーネントでコンテンツを渡す -->
      <MyButton @click="todoAdd">追加</MyButton>
      <MyButton @click="$emit('delete-done')">完了済みを削除</MyButton>
   </div>
</template>


<script>
import MyButton from './MyButton.vue'

export default {
  components: {
    MyButton,
  },
  emits:['delete-done','add-todo'],
  data() {
    return {
      newTodoText: '',
    }
  },
  methods: {
    todoAdd() {
      this.$emit('add-todo', this.newTodoText)
      this.newTodoText = ''
    },
  },
}
</script>

```




## 参考資料

今回、コンポーネントの理解を深めるために下記記事を参考にさせて頂きました。コンポーネントベースの開発手法は、コードの再利用性を高め、管理しやすくするために非常に効果的であることを体感することができました。

https://qiita.com/TakahiRoyte/private/a9aac2f069b99f3378c7

https://qiita.com/TakahiRoyte/private/5230b7b3685e0ab3756d