Skip to content

元件溝通

Props

在父元件綁定屬性給子元件後,子元件內還需要宣告 Props,才能取用綁定的屬性。

defineProps

使用 defineProps() 宣告 Props,定義 props 的名稱、型別,撰寫型別是讓程式易懂,也可以讓組件使用者知道傳遞的型別是否正確。

html
<script setup>
const props = defineProps({
  title: String,
  greetingMessage: String,
  likes: Number,
  abc: {
	type: String,
	required: true,
	default: 'success', // 可以是表達式
	// 可以自訂驗證
	validator:(value) => ['success', 'warning', 'danger'].includes(value)
  }
})
</script>
<script setup>
const props = defineProps({
  title: String,
  greetingMessage: String,
  likes: Number,
  abc: {
	type: String,
	required: true,
	default: 'success', // 可以是表達式
	// 可以自訂驗證
	validator:(value) => ['success', 'warning', 'danger'].includes(value)
  }
})
</script>
html
<script setup lang="ts">
const props = defineProps<{
  title?: string
  greetingMessage?: String,
  likes?: number,
  abc: {
	type: String,
	required: true,
	default: 'success', // 可以是表達式
	// 可以自訂驗證
	validator:(value) => ['success', 'warning', 'danger'].includes(value)
  }
}>()
</script>
<script setup lang="ts">
const props = defineProps<{
  title?: string
  greetingMessage?: String,
  likes?: number,
  abc: {
	type: String,
	required: true,
	default: 'success', // 可以是表達式
	// 可以自訂驗證
	validator:(value) => ['success', 'warning', 'danger'].includes(value)
  }
}>()
</script>

試試看

Emits

html
<template>
  <button @click="buttonClick('payload')">onclick</button>
  <button @click="$emit('submit', 'payload')">onclick</button>
</template>

<script setup>
const emit = defineEmits(['submit'])
const buttonClick = (e) => {
  const payload = e
  emit('submit', payload)
}
</script>
<template>
  <button @click="buttonClick('payload')">onclick</button>
  <button @click="$emit('submit', 'payload')">onclick</button>
</template>

<script setup>
const emit = defineEmits(['submit'])
const buttonClick = (e) => {
  const payload = e
  emit('submit', payload)
}
</script>

試試看

事件修飾符

vue
<template>
  <!-- 點擊事件的傳播將被停止 --> 
  <a @click.stop="doit"></a>
  <!-- 提交事件將不再重新加載頁面 -->
  <form @submit.prevent="doit"></form>
  <!-- 可以鏈接修飾符 --> 
  <a @click.stop.prevent="doit"></a>
  <!-- 只有修飾符 -->
  <form @submit.prevent></form>
  <!-- 只有當 event.target 是元素本身時才觸發處理程序 --> 
  <!-- 也就是說不理會來自子元素冒泡的事件 -->
  <div @click.self="doit"></div>
</template>
<template>
  <!-- 點擊事件的傳播將被停止 --> 
  <a @click.stop="doit"></a>
  <!-- 提交事件將不再重新加載頁面 -->
  <form @submit.prevent="doit"></form>
  <!-- 可以鏈接修飾符 --> 
  <a @click.stop.prevent="doit"></a>
  <!-- 只有修飾符 -->
  <form @submit.prevent></form>
  <!-- 只有當 event.target 是元素本身時才觸發處理程序 --> 
  <!-- 也就是說不理會來自子元素冒泡的事件 -->
  <div @click.self="doit"></div>
</template>

按鍵修飾符

任何來自KeyboardEvent.key的 key name 都可以作為修飾符,但要轉換為 kebab-case 的形式。

vue
<template>
  <!-- 只有當 key 是 Enter 時才調用 submit --> 
  <input @keyup.enter="submit" />
  <!-- Alt + Enter 組合 -->
  <input @keyup.alt.enter="clear" />
  <!-- PageDown 轉為 kebab-case 可作為修飾符 -->
  <input @keyup.page-down="onPageDown" />
</template>
<template>
  <!-- 只有當 key 是 Enter 時才調用 submit --> 
  <input @keyup.enter="submit" />
  <!-- Alt + Enter 組合 -->
  <input @keyup.alt.enter="clear" />
  <!-- PageDown 轉為 kebab-case 可作為修飾符 -->
  <input @keyup.page-down="onPageDown" />
</template>

provide

如果只用 props 需要傳遞好幾次: 使用 provide/inject:

使用方法

  • 上層元件使用 provide 提供;內層元件使用 inject 接。
  • reactive state 可以被 provide。 provide:
vue
<script setup>
import { ref, provide } from 'vue';

const count = ref(0);
// 可以 provide 任何種類,字串、數字、物件... etc
provide(/* key */ 'message', /* value */ 'hello!');
// 可以 provide reactive state
provide('key', count);
// 可以宣告為 readonly
provide('read-only-count', readonly(count));
</script>
<script setup>
import { ref, provide } from 'vue';

const count = ref(0);
// 可以 provide 任何種類,字串、數字、物件... etc
provide(/* key */ 'message', /* value */ 'hello!');
// 可以 provide reactive state
provide('key', count);
// 可以宣告為 readonly
provide('read-only-count', readonly(count));
</script>

inject:

vue
<script setup>
import { inject } from 'vue';

// 直接注入
const value = inject('message');
// 可以有預設值
const withDefaultValue = inject('message', 'default value');
// 可以從 function 計算出預設值
const withFactorFunction = inject('key', () => new ExpensiveClass(), true);
</script>
<script setup>
import { inject } from 'vue';

// 直接注入
const value = inject('message');
// 可以有預設值
const withDefaultValue = inject('message', 'default value');
// 可以從 function 計算出預設值
const withFactorFunction = inject('key', () => new ExpensiveClass(), true);
</script>

Working with Reactivity

  • 使用 provided state 和 mutations function 保持 reactivity。
  • 應盡量把 provided state 和 mutations function 集中在同一個處,易於維護(高內聚性),範例:
vue
<!-- provider component -->
<script setup>
import { provide, ref } from 'vue';

const location = ref('North Pole');

function updateLocation() {
  location.value = 'South Pole';
}

provide('location', {
  location,
  updateLocation,
});
</script>
<!-- provider component -->
<script setup>
import { provide, ref } from 'vue';

const location = ref('North Pole');

function updateLocation() {
  location.value = 'South Pole';
}

provide('location', {
  location,
  updateLocation,
});
</script>
vue
<!-- injector component -->
<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

<script setup>
import { inject } from 'vue';

const { location, updateLocation } = inject('location');
</script>
<!-- injector component -->
<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

<script setup>
import { inject } from 'vue';

const { location, updateLocation } = inject('location');
</script>

試看看

Expose

defineExpose() 暴露元件屬性,上層元件可透過元件參考取得。

vue
<template>
  <v-dialog v-model="model">
  </v-dialog>
</template>

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

const model = false
const showDialog = () => model = true

defineExpose({ showDialog });
</script>
<template>
  <v-dialog v-model="model">
  </v-dialog>
</template>

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

const model = false
const showDialog = () => model = true

defineExpose({ showDialog });
</script>

Summary

  • 元件定義 Props:使用 const props = defineProps() 定義元件 Props,以取用父元件綁定的屬性。
    • <script setup> 當中以 props.someprops 取得 props。
    • template 當中直接以 取得 props。
  • 元件事件定義:使用 const emits = defineEmits(['submit']) 定義元件事件。
    • template 當中以 $emit('submit', 'payload') 觸發事件。
    • <script setup> 當中以 emits('submit', 'payload') 觸發事件。
  • 元件定義 Expose:使用 defineExpose 定義元件暴露的屬性,上層元件透過元件參考取得。
  • 父元件事件控制
    • 父元件常用 .prevent.stop 等等事件修飾符控制事件傳遞。
    • 父元件常用按鍵修飾符接收特定按鍵事件。

Reference