前面章節,我們實現了父子元件之間的資料傳遞。大多數時候,我們以元件形式來構建頁面的區塊,會涉及到元件巢狀的問題,一層套一層。這種情況,很大概率需要將祖先的資料,傳遞給子孫後代去使用。我們當然可以使用父傳子的方式,使用屬性一級級往下傳,但這樣真得很麻煩。所以在Vue和Blazor都提供了祖孫傳值的方案。
插個話題:有人會問,子傳父是不是也可以一級級往上傳?當然可以,但你絕不要這麼去做。子傳父就已經夠繞了,不要為難自己。如果有這種需求,應該考慮資料狀態從元件中剝離出來,我們將在進階章節,一起學習“狀態管理”。
Vue中使用provide/inject這兩個API來實現祖孫傳值(這個概念被翻譯為依賴注入,為免混淆,建議叫注入為好),Blazor則使用CascadingValue元件和[CascadingParameter] 特性來實現。Vue的實現方式,統一在邏輯層,更加簡明統一,而且靈活。反觀Blazor,穿插於檢視層和邏輯層,比較混亂,也不靈活。下面我們通過以下兩個部分來學習:
- 傳遞單個值和多個值
- 如何修改傳遞資料
一、傳遞單個值和多個值
Vue和Blazor,都可以傳遞任意值以及多個值
//Vue==================================================================== //祖先元件Grandparent。通過provide提供資料,可以是任何值、任何響應式資料、任何方法 <template> <div class="grandparent"> <h1>這是是祖元件</h1> <Parent> //Parent元件裡巢狀著<Child></Child>,祖傳孫的任務就是將值傳給Child </Parent> </div> </template> <script setup> import Parent from './components/Parent.vue' import Child from './components/Child.vue' import {ref,provide} from 'vue' //以鍵值對的方式提供單個值,其中鍵key可以是字串,也可以是一個Symbol provide('name','functionMC') //如果要傳遞多個值,多次provide就可以 const address = ref('GuangZhou') provide('age',18) //提供一個響應式資料 provide('address',address) //一個key,對應多個響應式資料 const likeCode = ref(['C#','JS']) const likeFruit = ref(['apple','bananer']) provide('like',{likeCode,likeFruit}) </script> //子孫元件Child。任何需要資料的子孫元件,都可以inject注入資料,Parent元件一樣也可以注入。 <template> <div class="child"> <h1>紅色是孫元件</h1> <h4>姓名:{{name}}</h4> <h4>年齡:{{age}}</h4> <h4>地址:{{address}}</h4> <h4>喜歡程式碼:</h4> <ul><li v-for = "item in likeCodes">{{item}}</li></ul> <h4>喜歡水果:</h4> <ul><li v-for = "item in likeFruits">{{item}}</li></ul> <h4>老祖說:{{grandparentSay}}</h4> </div> </template> <script setup> import { inject } from 'vue' //直接通過鍵來注入 const name = inject('name') const age = inject('age') const address = inject('address') //接受一個鍵裡的多個值 const {likeCodes,likeFruits } = inject('like') //注入時可以設定預設值,如果找不到鍵,則使用預設值 const grandparentSay = inject('grandparentSay','還沒有說') </script>
//Blazor=================================================================================== //祖元件grandparent。 //在模板中使用<CascadingValue>元件傳值。比較麻煩,傳遞多值時要不斷的巢狀,元件放在最內層。 //推薦以Name-Value鍵值對的形式來傳遞,傳遞的資料,可以是值,也可以是屬性,可以是任何型別。 //CascadingValue也可以預設Name的方式傳值,接收時,需要使用型別來匹配,自行查文件,不推薦 <div class = "grandparent"> <h1>灰色是祖元件</h1> <CascadingValue Name="name" Value="@("functionMC")"> <CascadingValue Name="age" Value="@age"> <CascadingValue Name="likeFruits" Value="@likeFruits"> <Parent> //Parent父元件中巢狀著孫元件<Child></Child> </Parent> </CascadingValue> </CascadingValue> </CascadingValue> </div> @code { private int age = 18; private string[] likeFruits = new string[] { "apple", "bananer" }; } //子元件 <div class="Child"> <h1>紅色是孫元件</h1> <h5>姓名:@Name</h5> <h5>年齡:@Age</h5> <h5>喜歡的水果:</h5> <ul> @foreach (var item in LikeFruits) { <li>@item</li> } </ul> </div> @code { //CascadingParameter特性的Name引數,就是鍵 //可以設定預設值,當找不到鍵時,使用預設值 //強型別,提供和接收的資料型別要一致 [CascadingParameter(Name = "name")] private string Name { get; set; } = "MC"; [CascadingParameter(Name = "age")] private int? Age { get; set; } [CascadingParameter(Name = "likeFruits")] private string[]? LikeFruits { get; set; } }
二、如何修改傳遞資料
雖然Vue和Blazor都實現了祖傳孫,也都表現為類似鍵值對的特徵。但兩者有一個非常大的區別:
1、Vue中,如果傳遞的是響應式資料【值例外】,在子孫中修改的話,祖先的資料會同步更新。provide和inject的資料,是引用同一個資料。所以,如果修改傳值資料的話,都應將邏輯放在provide資料的地方,即祖元件,然後將資料和修改邏輯一起provide出去,如果孫元件需要修改資料,則通過呼叫inject過來的方法來完成。
2、而在Blazor中,我們是這麼接收資料的【[CascadingParameter(Name = "name")] private string Name { get; set; }】,可以看出,重新定義了一個屬性來接收值。所以,在子孫中修改的話,祖先的資料不會同步更新,因為它們是不同的兩個變數,這和通過屬性進行父傳子的表現類似。如果要修改資料,就只能在祖元件中修改了,子孫資料會同步更新
3、Vue的資料修改會靈活一些,但這種靈活要特別注意控制,而Blazor的設計更加嚴謹合理。如果Blazor也能像Vue一樣,直接在邏輯層提供聯級值,那這票我一定投Blazor
下面僅提供在Vue中修改資料的推薦方法:
//Vue //祖先元件Grandparent。 <template> <div class="grandparent"> <h1>灰色是祖元件</h1> <h1>{{look}}</h1> <Parent></Parent> </div> </template> <script setup> import Parent from './components/Parent.vue' import Child from './components/Child.vue' import {ref,provide} from 'vue' //一個鍵,提供一個響應式資料和相應的方法 const look = ref({height:170,weight:130}) function changeLook(){ look.value.height = 175 } provide('look',{look,changeLook}) </script> //子孫元件Child。 <template> <div class="child"> <h1>紅色是孫元件</h1> <h4>樣貌:{{look}}</h4> </div> <button @click="changeLook()">修改樣貌</button> //直接在子元件中呼叫祖先提供的方法 </template> <script setup> import { inject } from 'vue'; //通過解構方法,注入響應式資料及其方法 const {look,changeLook} = inject('look') </script>
【最後補充一個小技巧:如果provide/CascadingValue是在根元件提供資料,我們就可以傳遞全域性資料了,而且這個資料的生命週期是例項範圍的】