點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Event 從 Component 內傳出 Data

Sam Xiao's Avatar 2024-02-21

為了讓 Component 的 Reusability 更高,我們不會將呼叫 API 部分寫在 Component 內,而會將呼叫 API 部分寫在 Component 外部,如此我們必須從 Component 內傳出 Data。

Version

Vue 3.4

Event

header001

  • 將新增 todo 部分包進 TodoHeader component

App.vue

<template>
  <TodoHeader @addTodo="onAddTodo" />
</template>

<script setup>
import TodoHeader from '@/components/TodoHeader.vue'
import { addTodo } from '@/api/todos.js'

let onAddTodo = async (val) => {
  if (val === '') return

  await addTodo(val)
}
</script>

Line 2

<TodoHeader @addTodo="onAddTodo" />
  • 使用 TodoHeader component,此時 <input><button> 已經被包在 component 內
  • TodoHeader component 會將資料透過 addTodo event 往外拋

Component Naming Convention

  • Event:以 camelCase 組合 兩個單字
  • Event Handler:以 on + event名稱camelCase 命名

Line 9

let onAddTodo = async (val) => {
  if (val === '') return

  await addTodo(val)
}
  • 在 event handler 內呼叫 addTodo() API function 新增一筆 todo

TodoHeader

TodoHeader.vue

<template>
  <div class="box">
    <input type="text" v-model="newTodo" />
    <button @click="onAdd">Add</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

let newTodo = ref('')
let emit = defineEmits(['addTodo'])

let onAdd = () => emit('addTodo', newTodo.value)
</script>

<style scoped>
.box {
  border-style: solid;
  border-width: 2px;
  border-color: red;
  width: fit-content;
}
</style>

Line 3

<input type="text" v-model="newTodo" />
<button @click="onAdd">Add</button>
  • <input><button> 都搬到 component 內

Line 11

let newTodo = ref('')
  • newTodo state:儲存新輸入的 todo

Line 12

let emit = defineEmits(['addTodo'])
  • defineEmits():定義 event,其中 emit() 為 function

Line 14

let onAdd = () => emit('addTodo', newTodo.value)
  • 新增一筆 todo
  • 不再此呼叫 addTodo() API function,因為會把 component 寫死,變得無法在別處使用
  • 使用 emit() 觸發 addTodo event,並將資料傳出去

Options API

App.vue

<template>
  <TodoHeader @addTodo="onAddTodo" />
</template>

<script>
import TodoHeader from '@/components/TodoHeader.vue'
import { addTodo } from '@/api/todos.js'

export default {
  components: {
    TodoHeader
  },
  methods: {
    async onAddTodo(val) {
      if (val === '') return

      await addTodo(val)
    }
  }
}
</script>
  • 最後附上相同功能的 Options API 供參考
  • Component 除了要 import 外,還要在 components 下宣告

TodoHeader.vue

<template>
  <div class="box">
    <input type="text" v-model="newTodo" />
    <button @click="onAdd">Add</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    newTodo: ''
  }),
  methods: {
    onAdd() {
      this.$emit('addTodo', this.newTodo)
    }
  }
}
</script>

<style scoped>
.box {
  border-style: solid;
  border-width: 2px;
  border-color: red;
  width: fit-content;
}
</style>
  • 不必宣告 event,直接以 this.$emit() 呼叫即可

Conclusion

  • Event 最典型的用法就是將 data 拋出去,由呼叫端負責處理 API 部分,而不會在 Component 內呼叫 API