Blazor和Vue對比學習(基礎1.3):屬性和父子傳值

組件除了要解決視圖層展示、視圖層與邏輯層的數據綁定,還需要解決一個重大問題,就是在組件樹中實現數據傳遞,包括了父到子、子到父、祖到孫,以及任意組織之間。而我們上一章講到的實現雙向綁定的兩個指令,Vue的v-model,Blazor的@bind,可以認為是父到子和子到父,兩個方向同時實現的語法糖,後面章節我們再來實現它。

我們先從最簡單的父子傳值開始學習。在Vue和Blazor中,都是通過屬性來實現父子組件的數據傳遞。我們以往都是在html標籤上設置屬性,屬性值可以是字面量,也可以綁定邏輯數據。比如這樣<a :href=”href”>連接</a>。我們現在把a標籤換成一個自定義的組件,<Link :href=”href”>鏈接</Link>,Link組件會如何對待這個綁定的href值呢?【例子中,大家不要忽略一個事實,無論是a標籤也好,還是組件Link也罷,它們都是在另外一個組件中使用的,這個組件我們稱為父組件,而a標籤或Link組件,稱為子組件】

  1. 父子傳值的基本使用
  2. 傳遞屬性值的類型
  3. 傳遞屬性值的校驗

 

1、父子傳值的基本使用

Vue在子組件中,通過defineProps(宏命令,不需要import)來定義屬性,Blazor在子組件中,通過[Parameter]特性,來標註屬性。Vue不斷的創造新的API,不看源碼,你不會知道這個defineProps是怎麼工作的,而Blazor無招勝有招,使用的時候,你就能大概明白是怎麼一回事!

//Vue=====================================

//1、子組件Post

//定義兩個屬性用於接收數據
<script setup>
const props = defineProps(['title','content'])  //在程式碼中,通過props.title來使用屬性值
</script>

//在模板中使用接收到的兩個屬性值
<template>
    <h1>{{title}}</h1>
    <h3>{{content}}</h3>
</template>



//2、父組件傳遞數據
//引入子組件Post
<script setup>
import {ref} from 'vue'
import Post from './components/Post.vue'
const content = ref('美國又來攪局!')
</script>

//使用子組件,title屬性綁定字面量,content屬性綁定邏輯變數
<template>
  <Post title="俄烏局事有重大變化" :content="content"></Post>
</template>



//3、父組件傳遞數據-循環渲染
<script setup>
import {ref} from 'vue'
import Post from './components/Post.vue'
const posts = ref([
    {title:'標題1',content:'內容1'},
    {title:'標題2',content:'內容2'},
])
</script>

<template>
  <Post v-for="post in posts" :title="post.title" :content="post.content"></Post>
</template>
//Blazor===================================

//1、子組件Post
//在模板中使用接收到的兩個屬性值
<h1>@Title</h1>
<h3>@Content</h3>

//定義兩個屬性用於接收數據
@code {
    [Parameter]
    public string Title { get; set; }
    [Parameter]
    public string Content { get; set; }
}


//2、父組件傳遞數據
//使用子組件,title屬性綁定字面量,content屬性綁定邏輯變數
<Post Title="俄烏局事有重大變化" Content="@content"></Post>

@code {
    private string content = "美國又來攪局";
}


//3、父組件循環渲染傳遞數據
@foreach (var post in posts)
{
    <Post Title="@post.Title" Content="@post.Content"></Post>
}

@code {
    private List<PostModel> posts = new List<PostModel>
    {
        new PostModel{Title="標題1",Content="內容1"},
        new PostModel{Title="標題2",Content="內容2"}
    };
}

 

 

2、傳遞屬性值的類型

對於HTML標籤的屬性,我們只能傳遞指定的類型,一般是數值、字元串、布爾等簡單類型,但對於自定義組件,我們可以傳遞任意類型,Vue和Blazor在這方面都沒有限制。

【有個注意點需要特別小心】:

Vue:當傳遞數組、對象等引用類型時,僅僅傳遞了引用地址。所以,在子組件中直接修改傳遞過來的屬性值,父組件的值也會被改變。在Vue中,如果需要修改傳遞過來的屬性值,建議將屬性值賦值給一個新變數,再對新變數進行加工處理。

Blazor:不存在Vue的問題,即使傳遞引用類型,也是傳遞一個新值給子組件,屬性值在父子組件中相互獨立。

//Vue=====================================

//子組件,接收對象和數組
<script setup>
const props = defineProps(['value1','value2'])
</script>

<template>
    <h1>{{value1.name}}-{{value1.age}}</h1>
    <h3 v-for="item in value2">{{item.name}}-{{item.age}}</h3>
</template>


//父組件,傳遞對象和數組類型數據
<script setup>
import {ref} from 'vue'
import Post from './components/Post.vue'
const value1 = ref({name:'Fun',age:18})
const value2 = ref([
    {name:'Fun',age:18},
    {name:'MC',age:19},
])
</script>

<template>
  <Post :value1="value1" :value2="value2"></Post>
</template>
//Blazor

//子組件,定義了一個PostModel類屬性,以及這個類的集合屬性
<h1>@($"{PostModel.Title}-{PostModel.Content}")</h1>

@foreach (var item in PostModels)
{
    <h3>@($"{item.Title}-{item.Content}")</h3>
}


@code {
    [Parameter]
    public PostModel PostModel { get; set; }
    [Parameter]
    public List<PostModel> PostModels { get; set; }
}


//父組件創建了一個PostModel實例,以及PostModel的集合實例
<Post PostModel="@postModel" PostModels="@postModels"></Post>

@code {
    private PostModel postModel = new PostModel { Title = "標題1", Content = "內容1" };

    private List<PostModel> postModels = new List<PostModel>
    {
        new PostModel{Title="標題2",Content="內容2"},
        new PostModel{Title="標題3",Content="內容3"}
    };
}

 

 

3、傳遞屬性值的校驗

Blazor是天生強類型,屬性值的校驗非常簡單。而Vue中的屬性值校驗也麻煩些,如果不使用TS,只能支援運行時校驗,使用TS,結果volar,可以支援編譯時校驗。

//Vue=====================================

//運行時校驗
const props = defineProps({
  // 基礎類型檢查
  propA: Number,
  // 多種可能的類型
  propB: [String, Number],
  // 必傳,且為 String 類型
  propC: {
    type: String,
    required: true
  },
  // Number 類型的默認值
  propD: {
    type: Number,
    default: 100
  },
  // 對象類型的默認值
  propE: {
    type: Object,
    default() {
      return { message: 'hello' }
    }
  }
}



//藉助TS和volar實現編譯時檢驗

//泛型約束
const props = defineProps<{
  foo: string
  bar?: number
}>()

//使用介面
interface Props {
  foo: string
  bar?: number
}
const props = defineProps<Props>()

//默認值的話,比較麻煩,使用withDefault再包一層
const props = withDefault(defineProps<{
  foo: string
  bar?: number
}>(),{
  foo:'hello',
  bar:10,
})
//Blazor====================================

//類型、是否必填、默認值,一行搞定,而且都是c#本身的語法,不用藉助API,就問你爽不爽!?

@code {
    [Parameter]
    public PostModel? PostModel { get; set; } = new PostModel { Title = "默認標題", Content = "默認內容" };
    [Parameter]
    public List<PostModel> PostModels { get; set; }
}