元件除了要解決檢視層展示、檢視層與邏輯層的資料繫結,還需要解決一個重大問題,就是在元件樹中實現資料傳遞,包括了父到子、子到父、祖到孫,以及任意組織之間。而我們上一章講到的實現雙向繫結的兩個指令,Vue的v-model,Blazor的@bind,可以認為是父到子和子到父,兩個方向同時實現的語法糖,後面章節我們再來實現它。
我們先從最簡單的父子傳值開始學習。在Vue和Blazor中,都是通過屬性來實現父子元件的資料傳遞。我們以往都是在html標籤上設定屬性,屬性值可以是字面量,也可以繫結邏輯資料。比如這樣<a :href="href">連線</a>。我們現在把a標籤換成一個自定義的元件,<Link :href="href">連結</Link>,Link元件會如何對待這個繫結的href值呢?【例子中,大家不要忽略一個事實,無論是a標籤也好,還是元件Link也罷,它們都是在另外一個元件中使用的,這個元件我們稱為父元件,而a標籤或Link元件,稱為子元件】
- 父子傳值的基本使用
- 傳遞屬性值的型別
- 傳遞屬性值的校驗
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; } }