iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
SideProject30

從零開始的firebase與Vue框架ーSNS專案系列 第 26

Day26—Vue(十二)Prop / $emit 與 Provide / Inject

  • 分享至 

  • xImage
  •  

前言

今天也在實作前,先來了解vue傳值的方法。以下是使用 provide/inject、props 和 Vuex 等方法的圖例,今天先介紹官方提供的Prop和Provide / Inject這兩種方法。


圖片來源:Vue3 的資料狀態管理,provide / inject、vuex、props

  • provide/inject 主要應用在祖先元件和後代元件之間的通訊,能夠與響應式數據配合使用。
  • props 則主要用於父子元件之間的通訊,父元件透過 props 把資料傳遞給子元件。

Props

props是在子組件中聲明的屬性,通過父組件傳遞數據給子組件。

使用場景:適用於父子組件之間的簡單數據傳遞。

父組件

<script setup>
import { ref } from 'vue'
import BlogPost from './BlogPost.vue'
  
const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])

const postFontSize = ref(1)
</script>

<template>
	<div :style="{ fontSize: postFontSize + 'em' }">
    <BlogPost
      v-for="post in posts"
      :key="post.id"
      :title="post.title"
      @enlarge-text="postFontSize += 0.1"
    ></BlogPost>
  </div>
</template>

在父組件中,我們使用v-for迴圈遍歷 posts 陣列,並將每一個 BlogPost 子組件渲染出來。
再通過 :title="post.title" 將標題數據傳遞給子組件,並透過@enlarge-text監聽子組件觸發的自定義事件,當事件發生時,執行postFontSize += 0.1以更新字體大小。

子組件

<script setup>
defineProps(['title'])
defineEmits(['enlarge-text'])
</script>

<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

在子組件我們用了兩個函數:

  1. 使用 defineProps 定義了 title 屬性:讓他能夠接收父組件傳遞的標題數據。
  2. 使用 defineEmits 定義了一個 enlarge-text 的自定義事件,當按鈕被點擊時,通過 $emit('enlarge-text') 觸發這個事件,告知父組件執行相應的操作。

在官方的例子中,使用props將數據傳遞給子組件,以及使用 $emit 在子組件中觸發自定義事件,實現了父子組件之間的通信。

參考資料:組件基礎–傳遞props

Provide / Inject


圖片來源:官方文件—依赖注入

使用場景: 主要用於祖先和後代(爺孫等)組件之間的狀態傳遞。

父(提供者)組件

<!-- ParentComponent.vue -->
<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

const sharedData = ref('Hello from parent!')

provide('sharedData', sharedData)
</script>

在提供者組件中,我們使用 provide 函數將數據 sharedData 提供給後代組件。
provide 函數的第一個參數是提供的 key,這是一個唯一標識符,用於在注入者組件中檢索數據。第二個參數是要提供的實際數據。

provide() 函数接收两个参数。第一个参数被称为注入名,可以是一个字符串或是一个 Symbol。后代组件会用注入名来查找期望注入的值。一个组件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。
資料來源:官方文件—依赖注入

  • 也可以在整個應用層面像App.vue提供依賴
// 官方文件代碼
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

子組件

<!-- ChildComponent.vue -->
<template>
  <div>
    <p>{{ injectedData }}</p>
  </div>
</template>

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

const injectedData = inject('sharedData', 'Default Value')
</script>

在注入者組件中,我們使用 inject 函數獲取提供的數據。inject 函數的第一個參數是要注入的 key,它與提供者組件中的 key 相對應。第二個參數是默認值,如果找不到提供者,則使用默認值。

// 官方文件代碼
// 如果没有祖先组件提供 "message"
// `value` 会是 "这是默认值"
const value = inject('message', '这是默认值')

和響應式數據配合使用

当提供 / 注入响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。

資料來源:[官方文件—依赖注入]

  1. 先在提供者組件中動態provide:
<!-- 在供给方组件内 -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

//聲明並提供一個更改數據的方法函數
function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>

  1. 接著在注入方使用這個組件
<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'

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

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

總結

  • props:基本的父子組件傳值
  • provide/inject:適合爺孫,但在龐大的專案中還是推薦用vuex整合結構

參考資料

  1. [重構倒數第26天] — 你可能不需要Vuex ( You might not need Vuex )
  2. Vue 3 中的 Provide 與 Inject
  3. 你可能不知道的provide / inject 用法

上一篇
Day25—Vue(十一)Router、SPA與Vue-Router
下一篇
Day27—Vue(十三) Vuex 狀態管理模式
系列文
從零開始的firebase與Vue框架ーSNS專案31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言