如何在 Vue.js 中的組件之間傳遞數據

已發表: 2022-03-10
快速總結 ↬有這麼多不同的方式來跨組件共享數據,您應該知道哪種技術最適合您的情況。 下面我們來分析一下 VueJS 中最常見的三種傳遞數據的方式。

跨組件共享數據是 VueJS 的核心功能之一。 它允許您設計一個更加模塊化的項目,控制數據范圍,並在您的應用程序中創建自然的數據流。

除非您在一個組件中創建整個 Vue 應用程序(這沒有任何意義),否則您將遇到需要在組件之間共享數據的情況。

在本教程結束時,您將了解完成此任務的三種方法。

  • 使用 props 從父級到子級共享數據,
  • 發出自定義事件以從子級共享數據到父級,
  • 使用 Vuex 創建應用級共享狀態。

好的——讓我們開始吧!

使用 Nuxt 構建應用程序

使用 Spotify,您的朋友可以查看您正在播放的內容。 如果互聯網的其餘部分也能體驗到您的算法節奏怎麼辦? 了解如何使用 Vue.js 和 Nuxt 編寫您自己的應用程序來分享您在 Spotify 上收聽的內容。 閱讀相關文章 →

跳躍後更多! 繼續往下看↓

1. 使用道具從父母到孩子共享數據

VueJS props 是在組件之間共享數據的最簡單方法。 道具是我們可以賦予組件的自定義屬性。 然後,在我們的模板中,我們可以給這些屬性值,並且——BAM——我們將數據從父組件傳遞到子組件!

例如,假設我們正在處理用戶個人資料頁面並希望讓子組件接受用戶名道具。 我們需要兩個組件。

  1. 接受道具的子組件,我們稱之為AccountInfo.vue
  2. 傳遞 prop 的父組件,我們稱之為ProfilePage.vue

AccountInfo.vue中,我們可以使用 props 選項聲明它接受的道具。 所以,在組件選項中,讓我們讓它看起來像下面這樣。

 // AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>

然後,為了實際傳遞來自父級( ProfilePage.vue )的數據,我們像自定義屬性一樣傳遞它。

 // ProfilePage.vue <account-info username='matt' />

現在如果我們加載我們的頁面,我們可以看到我們的AccountInfo組件正確地呈現了其父級傳入的值。

與使用其他 VueJS 指令時一樣,我們可以使用 v-bind 動態傳遞 props。 例如,假設我們要將 username 屬性設置為等於一個變量。 我們可以通過使用 v-bind 指令的簡寫來實現這一點(或者:為 :)。 代碼看起來有點像這樣:

 <template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>

這意味著我們可以更改我們的數據,並且使用該值的任何子道具也將更新。

提示:始終驗證您的道具

如果你想編寫更清晰的 Vue 代碼,一個重要的技術是驗證你的 props。 簡而言之,這意味著您需要為您的道具指定要求(即類型、格式等)。 如果不滿足這些要求之一(例如,如果傳遞的道具類型不正確),Vue 將打印出警告。

假設我們希望我們的用戶名道具只接受字符串。 我們必須修改我們的 props 對象,使其看起來像這樣:

 export default { props: { username: String } }

在大型 Vue 應用程序中工作或設計插件時,驗證 props 是必不可少的。 它有助於確保每個人都在同一頁面上,並按照預期的方式使用道具。

有關我們可以在道具中包含的驗證的完整列表,我絕對建議您查看官方文檔以進行深入審查。

提示:遵循道具命名約定

根據 VueJS 風格指南,命名 props 的最佳方式是在腳本中聲明它們時使用camelCase ,在模板代碼中引用它們時使用 kebab-case。

這背後的原因其實很簡單。 在 Javascript 中, camelCase是標準命名約定,而在 HTML 中,它是 kebab-case。

因此,Vue 建議我們遵守每種語言的規範。 值得慶幸的是,Vue 能夠自動在兩種樣式之間進行轉換,因此開發人員無需進行額外設置。

 // GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }

2. 發出事件以從子級到父級共享數據

現在我們已經將數據傳遞到層次結構中,讓我們以另一種方式傳遞它:從子組件到父組件。 我們不能使用道具,但我們可以使用自定義事件和監聽器。

每個 Vue 實例都可以調用一個觸發事件的.$emit(eventName)方法。 然後,我們可以使用 v-on 指令以與其他任何方式相同的方式監聽此事件。

創建自定義事件

讓我們通過添加一個更改用戶名的按鈕來構建我們的用戶配置文件示例。 在我們的子組件( AccountInfo.vue )中,讓我們創建按鈕。

然後,當單擊此按鈕時,我們將發出一個名為changeUsername的事件。

 <template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>

在父級內部,我們處理此事件並更改user.username變量。 就像我們之前討論的那樣,我們可以使用 v-on 指令或簡稱“@”來監聽事件。

 <template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>

讓我們試試看。 您應該看到,當您單擊按鈕時,用戶名更改為“新名稱”。

提示:自定義事件可以接受參數

將參數傳遞給事件的最常見用例是當您希望子組件能夠為其屬性設置特定值時。 你永遠不想直接從組件本身編輯 prop 的值。

但是,幸運的是,我們可以在自定義事件中使用傳遞參數來使父組件更改值。

假設我們要修改changeUsername事件,以便我們可以向它傳遞一個值。

$emit方法採用可選的第二個參數作為參數。 所以我們所做的就是在我們的事件名稱之後添加我們的新用戶名值。

 this.$emit('changeUsername', 'mattmaribojoc')

然後,在我們的父組件中,我們可以使用特殊的$event變量內聯訪問這些值,或者我們可以編寫一個帶有參數的處理程序方法。

 <account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }

3.使用Vuex創建應用級共享狀態

好的——我們知道如何在父母/孩子之間共享數據,但是其他組件呢? 如果我們想傳遞數據,我們是否必須創建一個極其複雜的層次系統?

謝天謝地沒有。 多年來,出色的 Vuex 狀態管理庫一直在簡化開發人員的生活。 簡而言之,它創建了一個所有組件都可以訪問的集中式數據存儲。

在我們之前使用的方法(道具/發射事件)中,每個組件都有自己的數據狀態,然後我們在組件之間共享這些狀態。 但是,Vuex 讓我們將所有共享數據提取到每個組件都可以輕鬆訪問的單個狀態中。 這種共享狀態稱為存儲。

讓我們試試看。

因為 Vuex 與 Vue 的核心代碼是分開的,所以我們首先必須將其安裝並導入到我們的項目中。 首先,我們必須在項目 CLI 中運行npm install vuex --save

然後,使用包含以下代碼的 index.js 文件創建一個 src/store 文件夾。

 // store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });

要將它包含在我們的根 Vue 實例中,我們必須導入我們的 store/index.js 文件並將其傳遞給我們的 Vue 構造函數。

 // main.js import store from "./store"; new Vue({ store, ...

訪問組件內部的 Vue Store

由於我們將 Vuex 存儲添加到根 Vue 實例中,它會被注入到根的所有子節點中。 如果我們想從一個組件訪問 store,我們可以通過this.$store

現在,讓我們深入了解 Vuec 商店的四個部分中的每一個的細節。

1. 狀態

Vuex 狀態是一個包含應用程序級數據的對象。 所有 Vue 實例都可以訪問這些數據。

對於我們的商店,讓我們創建一個存儲更多用戶配置文件數據的用戶對象。

 export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });

我們可以像這樣在任何實例組件中訪問這些數據。

 mounted () { console.log(this.$store.state.user.username); },

2.吸氣劑

我們使用 Vuex getter 返回狀態數據的修改值。 考慮 getter 的一個好方法是將它們視為計算屬性。 例如,getter 和計算屬性一樣,緩存它們的結果,並且僅在修改依賴項時重新評估。

在我們之前的商店的基礎上,假設我們想要創建一個基於全名屬性返回用戶名字的方法。

 getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }

Vuex getter 屬性可用於store.getters對像上的組件。

 mounted () { console.log(this.$store.getters.firstName); }

提示:了解默認的 Getter 參數

默認情況下,Vuex getter 接受兩個參數。

  1. state — 我們應用程序的狀態對象;
  2. getters — store.getters 對象,這意味著我們可以調用我們商店中的其他 getter。

您聲明的每個 getter 都需要第一個 state 參數。 根據您設計代碼的方式,您的 getter 可以使用第二個“getters”參數相互引用。

讓我們創建一個姓氏獲取器,它只是從我們的全名狀態屬性中刪除我們的名字值。 這個例子需要 state 和 getters 對象。

 lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }

提示:將自定義參數傳遞給 Vuex Getter

getter 的另一個很酷的特性是我們可以通過讓我們的 getter 返回一個方法來傳遞自定義參數。

 prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));

3. 突變

突變是正確更改狀態對象值的唯一方法。 需要注意的一個重要細節是突變必須是同步的。

像 getter 一樣,mutations 總是接受 Vuex 狀態屬性作為它們的第一個參數。 它們還接受一個自定義參數(稱為有效負載)作為第二個參數。

例如,讓我們進行突變以將用戶名更改為特定值。

 mutations: { changeName (state, payload) { state.user.fullName = payload } },

然後,我們可以使用store.commit方法從我們的組件中調用此方法,並將我們的有效負載作為第二個參數。

 this.$store.commit("changeName", "New Name");

通常情況下,您會希望您的有效負載成為一個對象。 這不僅意味著您可以將多個參數傳遞給突變,而且由於對像中的屬性名稱,它使您的代碼更具可讀性。

 changeName (state, payload) { state.user.fullName = payload.newName }

有兩種不同的方法可以使用有效負載調用突變。

  1. 您可以將突變類型作為第一個參數,將有效負載作為第二個參數。
  2. 您可以聲明傳遞單個對象,其中一個屬性用於類型,另一個屬性用於有效負載。
 this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });

兩者的工作方式沒有真正的區別,因此完全取決於個人喜好。 請記住,在整個項目中始終保持一致總是最好的,所以無論您選擇哪個,堅持下去!

4. 行動

在 Vuex 中,動作與突變非常相似,因為我們使用它們來改變狀態。 但是,操作不會改變值本身。 相反,動作會提交突變。

此外,雖然 Vuex 突變必須是同步的,但動作卻不是。 例如,使用操作,我們可以在 API 調用之後調用突變。

儘管我們看到的大多數 Vuex 處理程序都接受狀態作為它們的主要參數,但動作接受一個上下文對象。 這個上下文對象允許我們訪問 Vuex 存儲中的屬性(例如 state、commit、getter)。

這是一個等待兩秒鐘然後提交changeName突變的 Vuex 操作示例。

 actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }

在我們的組件中,我們使用 store.dispatch 方法來運行我們的函數。 我們傳遞參數就像我們傳遞突變一樣。 我們聲明類型並在第二個參數中傳遞任何自定義參數。

 this.$store.dispatch("changeName", { newName: "New Name from Action" });

包起來

現在,您應該知道在 VueJS 中跨組件共享數據的三種不同方式:props、自定義事件和 Vuex 存儲。

我希望本教程能幫助您更深入地了解一些不同的 Vue 方法和最佳實踐。 讓我知道您是如何將它們實施到您的項目中的!

延伸閱讀

如果您有興趣更深入地了解每種技術的技術方面/功能,這裡有一些很好的起點。

  • Vuex 官方指南網站
  • 用於道具和自定義事件的 VueJS 文檔
  • “WTF 是 Vuex 嗎? Vue 應用程序數據存儲初學者指南”,Anthony Gore,Vue.js 開發人員