Blazor和Vue對比學習(基礎1.6):祖孫傳值,聯級和注入

functionMC發表於2022-05-11

前面章節,我們實現了父子元件之間的資料傳遞。大多數時候,我們以元件形式來構建頁面的區塊,會涉及到元件巢狀的問題,一層套一層。這種情況,很大概率需要將祖先的資料,傳遞給子孫後代去使用。我們當然可以使用父傳子的方式,使用屬性一級級往下傳,但這樣真得很麻煩。所以在VueBlazor都提供了祖孫傳值的方案。

插個話題:有人會問,子傳父是不是也可以一級級往上傳?當然可以,但你絕不要這麼去做。子傳父就已經夠繞了,不要為難自己。如果有這種需求,應該考慮資料狀態從元件中剝離出來,我們將在進階章節,一起學習“狀態管理”。

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是在根元件提供資料,我們就可以傳遞全域性資料了,而且這個資料的生命週期是例項範圍的】

相關文章