• Blazor和Vue对比学习(基础1.7):传递UI片断,slot和RenderFragment


    组件开发模式,带来了复用、灵活、性能等优势,但也增加了组件之间数据传递的繁杂。不像传统的页面开发模式,一个ViewModel搞定整个页面数据。

    组件之间的数据传递,是学习组件开发,必须要攻克的难关。这个章节,我们将一起学习如何将UI片断传递给子组件。子组件的UI片断,由父组件来提供,子组件接收到后直接渲染,这种场景的使用范围还是比较多的。我们之前对自定义组件的操作,一直都是在标签属性的位置,从来没有在标签体内容的位置搞过【这个位置】。这个位置,就是为传递UI模板片断准备的。Vue使用slot来接收,Blazor使用RenderFragment来接收。这两个使用的差异还是很大,【】是组件标签,在视图层中使用;【RenderFragment ChildContent{get;set}】是属性,在逻辑层使用。我们通过以下几节,来一起学习。

    • 匿名传递一个UI片断
    • 具名传递多个UI片断
    • UI片断的数据作用域(父子作用域数据):以创建一个简单的自定义表格组件为例

     

    一、匿名传递一个模板片断

    复制代码
    //Vue=====================================
    
    //父组件:可以传递任意UI片断,包括响应式数据
    
    
    
    
     
    
    //子组件
    
    复制代码
    复制代码
    //Blazor
    //父组件:可以传递任意UI片断
    普通文本
    响应式数据:@msg
    
        
    任意HTML标签和自定义组件标签
    @code { private string msg = "响应式数据"; } //子组件:使用RenderFragment类型属性接收。注意,匿名片断,属性名称必须为ChildContent,这是命名约定
    @ChildContent
    @code { [Parameter] public RenderFragment? ChildContent { get; set; } } //如果父组件未传入UI片断,如。子组件如何设置默认值? //子组件需要使用完整属性的方式来接收 //完整属性的私有字段部分,设置默认值。RenderFragment是委托类型,需要传入一个回调,参数类型为RenderTreeBuilder,名称必须为__builder。先记住语法,后面会解释 private RenderFragment childContent = (RenderTreeBuilder __builder) => {

    父组件如果不传入UI,则默认显示这句话

    }; [Parameter] public RenderFragment? ChildContent { get => childContent; set => childContent = value; }
    复制代码

     

      

     

    二、具名传递多个片断

    复制代码
    //Vue=====================================
    
    //父组件。
    //使用template标签来包装模板片断,并使用【v-slot:片断名称】来命名
    //命名还可以简写为【#片断名称】
    //未具名的片断,会按先后顺序统一传入到未命名的slot中。
    
    
    
    
    
     
    //子组件。使用【name="片断名称"】,来接收具名的片断。
    
    复制代码
    复制代码
    //Blazor====================================
    //父组件。
    //直接使用标签方式传递,标签名就是子组件的RenderFragment属性名。
    //Blazor的具名片断,语法比Vue更简洁
    
        

    这里是Header片断

    这里是Body片断

    这里是Footer片断
    @code { } //子组件 //具备的RenderFragment可以任意命名。匿名则只能使用ChildContent
    @Header
    @Body
    @Footer
    @code { [Parameter] public RenderFragment? Header { get; set; } [Parameter] public RenderFragment? Body { get; set; } [Parameter] public RenderFragment? Footer { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } }
    复制代码

     

     

     

    三、UI片断的数据作用域(父子作用域数据):以创建一个简单的自定义表格组件为例

    正常情况下,传递UI片断时,父子组件的数据作用域是相互隔离的,但有时候我们需要父子组件之间的数据能够串通,比如:

     

     1、子组件需要使用父组件的数据:这个比较简单,通过属性传递,咱们都学过

    1Vuepeoples>

    2Blazor@peoples>

     

    2、父组件需要使用子组件的数据:这个比较麻烦,Vue还好点,Blazor会比较复杂。我们详细说一下:

    复制代码
    //Vue=====================================
    //使用slot的属性传递,记住套路就可以,还是比较简单
    
    //匿名插槽情况
    //父组件:使用【v-slot="slotProps"】接收,其中slotProps是命名约定,不能修改
    
    
    
     
    //子组件:通过属性msg和count传递多个值
    
    
    
    //具名插槽情况
    
        
        
    
    //子组件传值
    "header" msg="‘这是header传的值’">
    "header" msg="‘这是footer传的值’">
    复制代码
    复制代码
    //Blazor===================================
    //需要使用到泛型RenderFragment,会比较晦涩一些。没关系,我们先从认识RenderFragment的本质开始。
    // RenderFragment的本质是一个委托, 它将 RenderTreeBuilder 作为委托入参,从而完成UI的渲染。
    
    //我们先看一下以下四种Razor文档的等效写法:
    
    
    //第一种:这是一个简单的Razor文件
    
    hello world
    //第二种:这个文件如果使用RenderFragment来实现,可以等价于:
    @HelloContent
    @code{ private RenderFragment HelloContent=(RenderTreeBuilder builder)=>{ builder.OpenElement(0,"div"); builder.AddContent(1, "hello world"); builder.CloseElement(); }; } //第三种:上面的写法比较繁索,RenderFragment的回调里还可以直接写HTML //注意这种方式,参数名称必须为__builder,这是约定名称 //看到第三种写法,能不能领会到从父组件传递子组件的实质? //父组件传递UI片断时,这个UI片断就是进入到RenderFragment的回调里
    @HelloContent
    @code { private RenderFragment HelloContent = (RenderTreeBuilder __builder) => {
    hello world
    }; } //第四种:RenderFragment还有一种泛型方式RenderFragment,可以实现传递数据到UI片断 //下例中,将字符串world,做为参数传递到渲染片断里。
    @HelloContent("world")
    @code { private RenderFragment<string> HelloContent = (msg) => (RenderTreeBuilder builder) => { builder.OpenElement(0, "div"); builder.AddContent(1, "hello " + msg); builder.CloseElement(); }; } //最后,我们通过父子组件传递UI片断的方式,来实现hello world //父组件 "string"> //指定泛型类型
    @context //context代表为子组件的msg
    @code { } //子组件 @typeparam T @ChildContent((T)msg) //将msg强制转化为T泛型 @code { [Parameter] public RenderFragment? ChildContent { get; set; } private object msg = "hello world"; }
    复制代码

     

     

    3、最后,我们通过一个自定义表格组件的案例,来结束UI片断传递的学习。这个案例中,我们希望实现如下功能:

    ①自定义表格组件的名称叫MyTable

    ②数据源在使用组件时传入

    ③表格的列数和列名,也在使用组件时再确定

    复制代码
    //Vue=====================================
    //定义两个具名插槽THead和TBody
    //以属性方式传入数据源peoples
    //TBody插槽,将行数据传回到父组件使用
    
    //父组件
    
    
    
    
    
    //子组件
    
    
    
    复制代码

     

    复制代码
    //Blazor====================================
    
    //父组件
    //使用THead和Tbody两个具名UI片断
    //【T="People"】指定子组件的泛型
    //context为子组件【@TBody(item)】中的item
    
    "People" TItems="@peoples">
        
            ID
            姓名
        
        
            @context.Id
            @context.Name
        
    
    
    @code {
        private List peoples = new List
        {
            new People{Id=1,Name="functionMC"},
            new People{Id=2,Name="Shine"},
            new People{Id=3,Name="Billing"}
        };
    
        private class People
        {
            public int Id { get; set; }
            public string? Name { get; set; }
        }
    }
    
    
    //子组件
    //定义了一个泛型T,数据源及其类型,以及行的类型,都应该由父组件传入
    //通过【@TBody(item)】,将子组件的item传回去
    //其实子组件的item来源于父组件的TItems,数据的流转有两个过程:
    //①父组件将所有人peoples传递给子组件
    //②子组件又将一个人people传递给父组件
    @typeparam T
    
    
            @THead
        
            @foreach (var item in TItems)
            {
                if (TItems is not null)
                {
                    @TBody(item)
                }
            }
        
    @code { [Parameter] public List? TItems { get; set; } [Parameter] public RenderFragment? THead { get; set; } [Parameter] public RenderFragment? TBody { get; set; } }
    复制代码

     

  • 相关阅读:
    JVM面试题
    将SpringBOOT项目 打成 war 包 并 部署到 Tomcat
    无代码开发校验条件入门教程
    光影交织:汽车穿越隧道的视觉盛宴
    如何使用SVN或者Git回滚代码,你必须知道的三种场景
    160. 相交链表
    ThreadLocal源码学习笔记
    Javassist-ConstPool常量池
    Qt5多线程<12>
    微信小程序:独家全新娱乐性超高的喝酒神器
  • 原文地址:https://www.cnblogs.com/functionMC/p/16278266.html