Yew 框架(二)子组件的创建和渲染

原创
2020/05/04 23:32
阅读数 8.8K

Yew 子组件的创建和渲染

前一篇 Yew框架(一) 应用初始化过程 我们了解一应用启动的过程,后续我将探索Yew中的一些功能是如何实现的,先来看看 子组件的创建渲染过程。

Yew 支持在组件的视图中嵌套组件,支持给子组件传递属性,下图是测试代码扩展前后的对比:

上面的代码主要是创建VChild对象,不是特别难,但要看懂它做了什么,为什么要这样做,还得先看 Properties 宏对组件的属性做了什么。

Properties 属性

这是我们自定义的Button组件的属性声明:

扩展后:

代码一下变多了。

首先是定义了一个 PropsWrapper,包含两个属性,但属性的类型都为Option类型,并且实现了Default特性,从后面的代码可以看到它就是PropsBuilder 用来存储过程信息的。

定义了两个结构体,都实现PropsBuilderStep特性,定义一个PropsBuilder结构体,有一个泛型参数,参数实现PropsBuilderStep。这是一个状态机在Rust的实现模式,用泛型参数来表示构建器的状态,通过构建器的方法来进行状态迁移,不同的状态可以调用不同的方法。

因为我们只有两个属性,所以 PropsBuilderStep 只有两个状态。PropsBuilderStep_missing_required_prop_text 是初始状态,该状态来可以调用 onclick 和 text 两个方法,来配置 builder,信息存储在 Builder 内部的 PropsWarpper 中。

值得注意的是,Yew 组件的属性是可以不传的,但可不可以不传是由属性的定义者来决定的,而不是调用者来决定的。如果定义一个属性为必传,那么调用者必须传入一个合适的值。如果定义为可不传,调用者可以决定传还是不传。Builder 中自动生成的方法参数上有区别,如果是可以不传的属性,其参数是Option类型。

为Props结构体实现 Properties 属性,只有一个builder方法,用来创建该属性的 Builder,最后由调用该Builder的build方法(该方法只能在 PropsBuilderStep_build 状态下调用,Builder的方法是逆序生成的,最后一个方法调用完成后进入 build 状态),用 PropsWrapper内存储的值来创建一个 Props 对象。

现在接着看 VChild 的创建。

创建 VChild 对象

VChild对象有一个泛型参数,是子组件的类型,这里是 Button。扩展后的创建代码如下:

调用 VChild::new() 方法,传入三个参数,第一个是通过构建器构建的组件属性,第二个是默认的 NodeRef, 第三个是 None。

从代码中看到构建方法的参数都用 VComp 的transform方法进行了转换,这是为何?

Transformer

从注释中得知这个特性功能是用来把属性(调用者传入的属性值)转换了期待的类型(子组件属性对象中对应属性期待的类型)。

VComp 实现多种类型转换,让父组件可以有多种传递属性给子组件的方式,简单说就是提供了“语法糖”。从 FROM 到 TO,表示当子组件的属性期待的是 TO 类型时,父组件可以为其提供 FROM 类型,由 Transformer 自动转换为 TO 类型,赋值给属性。

搜索源码,VComp一共有7个实现:

  • 子组件需要什么,父组件就传什么;
  • 当子组件需要 T 类型时,可以传入 &T,要求 T 具有 Clone 特性;
  • 如果子组件需要的是 String,父组件可以传入 &str;
  • 如果子组件需要的是 Option<T> 类型,父组件可以只传入 T 或 &T(要求 T 具有 Clone 特性)类型;
  • 如果子组件需要的是 Option<String> 类型,父组件可以传入 &str 或 Option<&str>。

在示例中,我们需要的是一个 String 类型和一个 Option<Callback<()>> 类型,父组件传入的是 &str 和 Callback<()> 类型。经过Transformer 转换成了我们期望的类型。

这个功能非常好用,之前学习另一个框架 simi 时,发现我希望传递给子组件的数据有时候是 Option<T>,有时候是T,有时候是 str,有时候是String,但它的子组件只能接收相同类型的值,在使用的时候要手动做转换,比较麻烦,代码写出来很不好看,当时想过为simi贡献一个补丁来解决这个问题,但一直没有找到合适的方案。这里 Transformer 利用Rust的自动类型推导和泛型能力很优雅的解决了这个问题,非常不错。

VChild::new()

这个方法很简单,就是创建一个 VChild 对象。

转化为 VComp

第一张图51行,表示最终会调用VNode::from方法:

内部先调用 VComp::from 方法将 VChild 转换为 VComp,最终转换为 VNode::VComp。

如此看来,VChild 也是一个备胎,它也只是在中途被用一下,在这里就被抛弃了,才创建出来没有两分钟,哈哈哈!重头在 VComp::new()

果然如此,代码长度说明一切!

Generator

用来根据 GeneratorType 渲染子组件视图并挂载到 Dom 的闭包。GeneratorType 有两种情况,一种是创建组件对象并挂载到节点,另一种是之前已经挂载过该组件对象了,只需要更新组件的属性。

闭包内的代码已经很熟悉了(参考 Yew框架(一) 应用初始化过程),第一种情况下,和挂载任意VNode类似,第二种情况下发出一个更新指令到调度器,会先更新组件Scope的状态(期间会调用组件的 change 方法)并重新渲染视图。

创建一个可以创建或更新子组件的闭包,但并未执行,将VComp置为 Unmounted的状态。在渲染视图时,会根据具体情况决定如何来渲染该VComp。

展开阅读全文
加载中

作者的其它热门文章

打赏
2
2 收藏
分享
打赏
9 评论
2 收藏
2
分享
返回顶部
顶部