Blazor和Vue對比學習(基礎1.6):祖孫傳值,聯級和注入
- 2022 年 5 月 11 日
- 筆記
- Blazor, Maui/Blazor/Vue/uniapp, VUE
前面章節,我們實現了父子組件之間的數據傳遞。大多數時候,我們以組件形式來構建頁面的區塊,會涉及到組件嵌套的問題,一層套一層。這種情況,很大概率需要將祖先的數據,傳遞給子孫後代去使用。我們當然可以使用父傳子的方式,使用屬性一級級往下傳,但這樣真得很麻煩。所以在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是在根組件提供數據,我們就可以傳遞全局數據了,而且這個數據的生命周期是實例範圍的】