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

functionMC發表於2022-05-07

元件除了要解決檢視層展示、檢視層與邏輯層的資料繫結,還需要解決一個重大問題,就是在元件樹中實現資料傳遞,包括了父到子、子到父、祖到孫,以及任意組織之間。而我們上一章講到的實現雙向繫結的兩個指令,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; }
}

 

相關文章