Vue3筆記 | 元件與資料傳遞

更新於 發佈於 閱讀時間約 17 分鐘

Vue 的整個大架構核心是元件 (component),藉由把會重複利用的邏輯、樣式、內容封裝到一個個組件之中,來提高程式碼複用性以及開發效率。簡單來說,一個 Vue 建立的網頁就是由許多組件一個個拼出來的,然後資料在各個組件中穿梭來改變渲染我們的畫面。

所以本篇核心為 Vue 的元件以及資料傳遞。



元件

讓我們先在 src/components 下建立一個 PeopleBlock.vue 的檔案,這之後就會是我們第一個元件。

我們先在 PeopleBlock.vue 中寫一點東西:

<script setup>
</script>

<template>
<h1>I am a component</h1>
</template>

然後回到 App.vue 中來引入剛剛建立的元件,此時 App.vue 相對於 PeopleBlock.vue 來說就是父元件。引入成功會看到畫面改變啦!

<script setup>
import PeopleBlock from './components/PeopleBlock.vue';
</script>

<template>
<PeopleBlock />
</template>
raw-image

這種是元件引入的基本用法,但如果說今天 PeopleBlock.vue 要在很多元件中引入呢?每個元件都寫一次 import好麻煩,所以針對這類情形可以直接修改 main.js。

原本的 main.js 內部長這樣:

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

現在我們要把它拆開改成這樣:

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import PeopleBlock from './components/PeopleBlock.vue'

const app = createApp(App)
app.component('PeopleBlock', PeopleBlock)

app.mount('#app')

這樣我們可以直接在 App.vue 中直接使用 PeopleBlock.vue 而不用 import



資料傳遞

Props

如果你跟我一樣從 React 過來的,那看到這個名詞會覺得很熟悉,這就是父元件將資料傳給子元件的方式,注意,是父元件傳給子元件

現在假設我們父元件 App.vue 中有一筆資料要傳到 PeopleBlock.vue 中套用它的格式,那麼在兩個檔案中我該如何寫?

App.vue:

<template>
<PeopleBlock name="Jeremy"/>
</template>

PeopleBlock.vue:

<script setup>
const props = defineProps(['name'])
</script>

<template>
<h1>My name is: {{ props.name }}</h1>
</template>

你會發現父元件中給的名字 Jeremy 成功傳給子元件做出了渲染。

raw-image

在 Vue 中靠 defineProps 在子組件中定義接收到父元件資料,可以以陣列或物件的方式呈現。物件的寫法比較嚴謹一些,剛剛的宣告改成物件可以這樣寫:

const props = defineProps({
name:{
type: String,
default: ''
}
})

emit

在講 emit 之前,我要先提一個我稍早跟 senior 討論的問題:到底能不能把 function 在 Vue 中作為 props 傳遞給子元件?

從 React 過來的人應該通常都有這問題吧!因為 React 就是這樣做啊,我為什麼還要用一個 emit 來做傳遞?而且實際操作也可以喔,請看:

App.vue:

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

const name = ref('Jeremy')

const changeName = (newName)=> name.value = newName
</script>

<template>
<PeopleBlock :name="name" :change-name="changeName"/>
</template>

PeopleBlock.vue:

<script setup>
const props = defineProps({
name:{
type: String,
default: ''
},
changeName:{
type: Function,
default: () => {}
}
})
</script>

<template>
<h1>My name is: {{ props.name }}</h1>
<button @click="changeName('Joanna')">click</button>
</template>

會發現實際上當我點擊了按鈕後,名字從 Jeremy 變成 Joanna 了,一切貌似運作正常。而且 Mike 老師也在影片中特別說這樣單向資料流的方式便於維護管理。

但是喔,就是這個但是,Vue 不推薦你這樣做。部分文章也指出這樣做第一個有效能的問題,第二個是 Vue 看心情可能會渲染不出來改變的資料,所以為了避免這種賭運氣的事件,公司的 senior 還是統一用 emit了。

所以現在來看看 Vue 不同於 React 的做法,有一個口訣:

Props in, Events out

Props 大家很熟悉,events out 靠的就是 emit,是由子組件把事件傳給父元件,然後由父組件去監聽這個事件來決定要幹什麼。

比如剛剛的 code,改成 emit 操作會是這樣的:

先改 PeopleBlock.vue:

<script setup>
const props = defineProps({
name:{
type: String,
default: ''
}
})

defineEmits(['update-name'])
</script>

<template>
<h1>My name is: {{ props.name }}</h1>
<button @click="$emit('update-name')">click</button>
</template>

解釋一下就是我在子元件中建立一個客製化事件叫做 update-name,並透過 $emit 將這個事件傳給父元件。

App.vue 修改如下:

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

const name = ref('Jeremy')

const changeName = (newName)=> name.value = newName
</script>

<template>
<PeopleBlock :name="name" @update-name="changeName('Joanna')"/>
</template>

在父元件中透過 @update-name 接收從子元件傳來的事件,若事件被觸發就套用父元件本身設定的 changeName 函式更新名字。

這就是 Vue 有別 React 的資料傳遞方式,這種模式還會在之後的 v-model 在元件中的傳遞也息息相關,是一個必須要搞懂的地方。

v-model

過往在取得表單的資料通常都要寫一大串的 DOM 抓取或是監聽 change 或 input 事件,而在 Vue 中,這些事情變得非常簡單,靠的就是 v-model

我們來建立一下一個 <input> 嘗試看看:

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

const name = ref('Jeremy')
</script>

<template>
<input type="text" v-model="name">
<h1>{{ name }}</h1>
</template>

可以輕鬆發現隨著我在輸入框的改變,<h1> 渲染的畫面也會跟著改變,這表示 v-model 確實地把這個 <input> 跟我的 name 這個 ref 項目綁在一起了,這就是 Vue 在表單處理上強大的地方。

你甚至可以把 v-model 用在 check box、textarea、radio…等地方。

而且 v-model 還提供三種修飾福來幫助處理表單:

  1. .lazy:這個會把 Vue 的同步更新事件從預設的 input 改成 change,也就是你在輸入的時候並不會立即觸發畫面更新。
  2. .number:把輸入資料轉成數字,但如果開頭為0,他會幫你拿掉。
  3. .trim:我感覺最有用的地方,去除首尾空白!再也不用取出input value 後才寫 trim 了!

但是最前面講到的,Vue 是一個元件組成的事件,有時甚至會把這樣一個 <input> 單獨寫成一個元件,那 v-model 勢必得在元件中傳遞,該怎麼做呢?回歸剛剛講到的 Props in, Events out,來看一下如何利用這個口訣解決問題。

先創個 InputArea.vue 元件然後先不寫任何資料:

InputArea.vue:

<script setup >
</script>

<template>
<input type="text">
<h1></h1>
</template>

App.vue:

<script setup>
import InputArea from './components/InputArea.vue'

</script>

<template>
<InputArea/>
</template>

現在我們來建立資料進行傳遞:

App.vue:

<script setup>
import { ref } from 'vue';
import InputArea from './components/InputArea.vue'

const name = ref('Jeremy')
</script>

<template>
<InputArea v-model="name"/>
</template>

父元件的操作很簡單,綁 v-model 就對了。

InputArea.vue:

<script setup >
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
<input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
<h2>{{ modelValue }}</h2>
</template>

我們在子元件上定義接收資料的 props 與傳送事件的 emit,然後在 <input> v-vindvalue 屬性和 input 事件,input 事件中帶上我們自定義的事件,這樣就成功讓 v-model 在兩個元件中傳遞。

那能不能綁定多個 v-model呢?讓我抄一下官網的 code :

App.vue:

<script setup>
import { ref } from 'vue';
import InputArea from './components/InputArea.vue'

const first = ref('Jeremy')
const last = ref('Ho')
</script>

<template>
<InputArea
v-model:first-name="first"
v-model:last-name="last"/>
</template>

InputArea.vue:

<script setup>
defineProps({
firstName: String,
lastName: String
})

defineEmits(['update:firstName', 'update:lastName'])
</script>

<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
<h1>{{ firstName }} {{ lastName }}</h1>
</template>

你可能注意到,上面兩個 v-model 的範例中,App.vue 中對 v-model 的綁定寫法不太一樣。一個是直接 v-model="變數",一個是 v-model:名字="變數",對於前面那種作法,你在子元件中 defineProps 時就只能用 modelValue,而後面那一種允許你寫自己定義的名字,這是差別所在。



參考資料

  1. Vue.js 官方網站
  2. Vue3 + Vite 快速上手 Get Startrd EP3 - components / props / emit
  3. Vue3 + Vite 快速上手 Get Startrd EP4 - v-model 資料的雙向綁定 / 自訂義組件的資料綁定 / One-Way Data Flow 單向資料流
  4. Vue 中,如何将函数作为 props 传递给组件
  5. 父子組件資料傳遞 props、$emit
avatar-img
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Jeremy Ho的沙龍 的其他內容
Vue3 學習筆記,專案建立與基礎響應式篇
AWS 佈署簡單操作以及 RDS 建立篇
AWS 部署學習 - Day1,註冊與安裝 EB
記錄一下 API 串接的四種方式 www
有關 git add, git commit, git push
Vue3 學習筆記,專案建立與基礎響應式篇
AWS 佈署簡單操作以及 RDS 建立篇
AWS 部署學習 - Day1,註冊與安裝 EB
記錄一下 API 串接的四種方式 www
有關 git add, git commit, git push
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
組件組成一棵樹狀結構,類似於嵌套的 HTML 元素,但 Vue 提供了自定義內容和邏輯的封裝。通常我們會在專用的 .vue 文件中定義組件,並使用 <script setup> 來輕鬆管理狀態和事件。組件可以重複使用,並透過 props 傳遞數據,使用插槽實現內容分發。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
一般而言,組件之間的資料傳遞,可以使用 props 來達成,不過一旦層級過多的時候,props 就要逐層向下傳遞,會越來越麻煩且複雜。 而 provide、inject 可以解決這個問題,它可以提供一個「源頭」,子組件們可以藉由同一個源頭取得對應的資料,且沒有層級分別,都可以取得,就不用逐層傳遞資
當 父組件 有數據想傳送到 子組件 就可以使用props 1​. 父層傳遞設置 可以在父組件的屬性給予一個值,當作要傳送到子組件的資料。 父層組件​ : <!-- App.vue (父組件) --> <template> <div> <ChildComponent greetin
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
組件之間的通信是 Vue 應用開發中的一個重要方面。Vue 提供了一種名為事件發射(emit)的機制,讓子組件能夠向父組件發送消息。本文將介紹 Vue 中的事件發射(emit)機制,並通過實際範例演示其用法。
Thumbnail
在 Vue 中,組件是構建應用程式的基本單位,而 props 是組件間傳遞資料的主要方式之一,本文將介紹 Vue 中的 props,並通過實際範例展示如何使用 props 實現組件間的資料傳遞。
Thumbnail
v-model 指令是 Vue 中一個非常重要的功能,它實現了表單輸入和應用狀態之間的雙向綁定。本文將介紹如何使用 v-model 指令。
Thumbnail
Vue 的單文件組件(Single File Components,SFC)是一種特殊的文件格式,讓我們可以將Vue 組件的模板、邏輯和樣式都寫在單一個文件當中。本文將介紹 Vue SFC,幫助開發者理解和使用 Vue SFC。
Thumbnail
Vue 是尤雨溪所開發的一個前端框架,Vue 提供了響應式數據綁定和組件化的開發模式,能方便開發者構建出具有良好交互性的前端應用。而 Vite 則是一個前端構建工具,用途是提供更快的開發體驗。然後,本文將介紹如何使用 Vite 建立一個 Vue 專案的開發環境,這是你學習 Vue 的第一步。
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
組件組成一棵樹狀結構,類似於嵌套的 HTML 元素,但 Vue 提供了自定義內容和邏輯的封裝。通常我們會在專用的 .vue 文件中定義組件,並使用 <script setup> 來輕鬆管理狀態和事件。組件可以重複使用,並透過 props 傳遞數據,使用插槽實現內容分發。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
一般而言,組件之間的資料傳遞,可以使用 props 來達成,不過一旦層級過多的時候,props 就要逐層向下傳遞,會越來越麻煩且複雜。 而 provide、inject 可以解決這個問題,它可以提供一個「源頭」,子組件們可以藉由同一個源頭取得對應的資料,且沒有層級分別,都可以取得,就不用逐層傳遞資
當 父組件 有數據想傳送到 子組件 就可以使用props 1​. 父層傳遞設置 可以在父組件的屬性給予一個值,當作要傳送到子組件的資料。 父層組件​ : <!-- App.vue (父組件) --> <template> <div> <ChildComponent greetin
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
組件之間的通信是 Vue 應用開發中的一個重要方面。Vue 提供了一種名為事件發射(emit)的機制,讓子組件能夠向父組件發送消息。本文將介紹 Vue 中的事件發射(emit)機制,並通過實際範例演示其用法。
Thumbnail
在 Vue 中,組件是構建應用程式的基本單位,而 props 是組件間傳遞資料的主要方式之一,本文將介紹 Vue 中的 props,並通過實際範例展示如何使用 props 實現組件間的資料傳遞。
Thumbnail
v-model 指令是 Vue 中一個非常重要的功能,它實現了表單輸入和應用狀態之間的雙向綁定。本文將介紹如何使用 v-model 指令。
Thumbnail
Vue 的單文件組件(Single File Components,SFC)是一種特殊的文件格式,讓我們可以將Vue 組件的模板、邏輯和樣式都寫在單一個文件當中。本文將介紹 Vue SFC,幫助開發者理解和使用 Vue SFC。
Thumbnail
Vue 是尤雨溪所開發的一個前端框架,Vue 提供了響應式數據綁定和組件化的開發模式,能方便開發者構建出具有良好交互性的前端應用。而 Vite 則是一個前端構建工具,用途是提供更快的開發體驗。然後,本文將介紹如何使用 Vite 建立一個 Vue 專案的開發環境,這是你學習 Vue 的第一步。