当前位置:首页 > 科技  > 软件

来自 Rust 生态的强烈冲击?谈谈 Leptos 在语法设计上的精妙之处

来源: 责编: 时间:2024-05-24 17:25:15 275观看
导读过去很长一段时间,前端框架们都在往响应式的方向发展。大家都在基于 signal 实现自己的底层。这种趋势看上去非常火热,给人一种前端框架不往这个方向发展就落后了一样。同时又由于 React hooks 的深远影响,函数式 + 响应

uEU28资讯网——每日最新资讯28at.com

过去很长一段时间,前端框架们都在往响应式的方向发展。大家都在基于 signal 实现自己的底层。这种趋势看上去非常火热,给人一种前端框架不往这个方向发展就落后了一样。uEU28资讯网——每日最新资讯28at.com

同时又由于 React hooks 的深远影响,函数式 + 响应式成为了不少前端心中最理想的前端框架模样。Solid 成为了这种模式里最具代表性的框架。uEU28资讯网——每日最新资讯28at.com

但是,尽管如此,我依然对他保持一种不太愿意接纳的态度,并不是说我对 solid 不熟悉,或者抗拒接受新的知识,其根本原因,还是在语法设计上的问题。uEU28资讯网——每日最新资讯28at.com

基于响应式能实现细粒度更新,抛去虚拟 DOM 的 diff 成本,性能能够得到很大的提升。这种设想其实非常美好,但是,在语法设计上会面临巨大的挑战。uEU28资讯网——每日最新资讯28at.com

一、Solid.js

我们来观察并分析一下 solid.js 在语法设计上存在的问题。uEU28资讯网——每日最新资讯28at.com

function Counter() {  const [count, setCount] = createSignal(0)  return <div onClick={() => setCount(count() + 1)}>    Count: {count()}  </div>}

在这个案例中,我们可以使用 createSignal 创建一个响应式数据。这里的问题就在于,返回的响应式数据 count 他不是一个数据,而是一个获取数据的 getter 方法。uEU28资讯网——每日最新资讯28at.com

因为底层基于 Proxy 来实现,我们需要监听到数据的变化,那么就需要借助 Proxy 中的 getter 方法来实现,因此反馈到语法上,count 就只能是一个函数。uEU28资讯网——每日最新资讯28at.com

当我们想要将其渲染到 JSX 中时,在 solid 中就将其设计成 {count()}。这里设计成 count() 是沿用了 React 对于 JSX 的理解,想要传入一个值给 JSX。uEU28资讯网——每日最新资讯28at.com

当我们在点击事件中使用该响应式数据时。uEU28资讯网——每日最新资讯28at.com

setCount(count() + 1);

如果你要精准理解 count(),那么理解成本就有点高了,这里的 count() 执行,表达了两层含义。uEU28资讯网——每日最新资讯28at.com

初始化时,count() 表示会隐式的收集依赖。在跟踪范围内,调用 getter 会导致调用 getter 的函数依赖于对应的 signal。当 signal 更新时,这些依赖都会被重新执行。uEU28资讯网——每日最新资讯28at.com

更新时是依赖重新执行,不只是 count() 重新执行。许多人理解成 count 重新执行,那么在语义上会有更进一步的冲突。uEU28资讯网——每日最新资讯28at.com

例如:uEU28资讯网——每日最新资讯28at.com

const double_count = () => count() * 2// 或者在 jsx 中<div>count: {count()}</div>

更新时,当我们通过点击等行为触发更新,此时当我们使用 count(),则只是简单的计算出 count 当前的值。uEU28资讯网——每日最新资讯28at.com

setCount(count() + 1);

这里其实就是语法设计上的冲突问题。同样的函数执行,由于编译手段的强势侵入,在不同的场景里表达了不同的含义。uEU28资讯网——每日最新资讯28at.com

其实 solid.js 的开发团队也希望 count 就像是直观表达的那样,他不是一个 getter,而就是直接是一个值,因此就有类似于如下的语法设计uEU28资讯网——每日最新资讯28at.com

// 这个时候就变得正常了setCount((count) => count + 1);

但是很显然,如果直接完全像 React 那样符合直觉的语法设计,响应式的能力就得不到保证了。因此这是拥抱响应式不得不做出的牺牲。uEU28资讯网——每日最新资讯28at.com

Solid 的这个语法割裂,在组件传参的语法设计中,表现得尤为明显。uEU28资讯网——每日最新资讯28at.com

例如你看下面这段代码,令人意外的是,props.msg 是可以具备响应性的,当我还不熟悉 Solid 的时候直接大吃一惊。uEU28资讯网——每日最新资讯28at.com

function Message(props: Props) {  return <div>    <h1>      hello, this message is: {props.msg}    </h1>    <Child />  </div>}

这是拥抱响应式的无奈之举。因为在组件传参的时候,其实可能存在两种类型,一种类型是普通数据,例如:uEU28资讯网——每日最新资讯28at.com

<Message msg='hello world' />

而另外一种,就是响应性数据,例如:uEU28资讯网——每日最新资讯28at.com

<Message msg={msg()} />

如果我希望一个字段,他可以传普通类型、也可以传响应性类型,那么问题就来了,子元素内部如何判断父组件到底会传什么类型过来呢?uEU28资讯网——每日最新资讯28at.com

solid 的解决方案就是,只允许在父组件传参时,这样写 {msg()}。下面这种写法就会报错。uEU28资讯网——每日最新资讯28at.com

<Message msg={msg} />

uEU28资讯网——每日最新资讯28at.com

这样做的好处就是 solid 可以利用编译手段去识别 msg() 然后深入子组件内部做依赖收集,从而让子组件内部不需要做额外的判断。但是付出的代价就是语法割裂更严重了。uEU28资讯网——每日最新资讯28at.com

除此之外,正因为有黑科技的强势侵入,因此 solid 中的 JSX 与 React 中的 JSX 表现并完全不一致,也不能按照常规的表达式去理解。uEU28资讯网——每日最新资讯28at.com

语法的割裂是我不愿意拥抱 Solid 的主要原因。uEU28资讯网——每日最新资讯28at.com

二、Leptos

让我们来看看 rust 生态中,同样是基于 signal 来实现的响应式框架 Leptos 是如何在语法设计上解决 solid 的割裂问题的。uEU28资讯网——每日最新资讯28at.com

首先,一个非常巧妙的设计就是,在 rsx 中,状态传入的括号中,直接接收的就是一个函数uEU28资讯网——每日最新资讯28at.com

#[component]fn App() -> impl IntoView {  let (count, set_count) = create_signal(0);  view! {    <div>{count}</div>  })

这里类似于 React 的 render propsuEU28资讯网——每日最新资讯28at.com

这样看着就非常的舒服。因为声明的 count 是一个函数,模板渲染中需要的也是一个函数,语法表现就很一致,按照这个设计,我们就可以不用写 count() 了。uEU28资讯网——每日最新资讯28at.com

这个小的语法设计细节的调整,让整个语法都变得更加一致。uEU28资讯网——每日最新资讯28at.com

当我们更新时uEU28资讯网——每日最新资讯28at.com

set_count.update(|count| *count += 1)

当我们要往子组件中传递参数时uEU28资讯网——每日最新资讯28at.com

<ProgressBar progress=count />

当语法规则发生一些简单的调整,我们会发现,在大多数情况下,count 的使用都保持了一致性,而不是像 solid 那样在不同的场景之下有不同的行为。uEU28资讯网——每日最新资讯28at.com

当然,如果我们要在逻辑中获取到 count 的值时,仍然需要使用 count() 来达到目的。不过这在语义上是没有冲突的。uEU28资讯网——每日最新资讯28at.com

let double = move || count() * 2;

与 solid 一样,这段代码类似于计算属性,这个匿名函数也会被收集成为一个依赖,从而让 double 也具备响应性。uEU28资讯网——每日最新资讯28at.com

当我们往组件内部传参数时,rust 可以通过定义参数宏来接收和设置参数的类型、默认值等。uEU28资讯网——每日最新资讯28at.com

#[component]pub fn ProgressBar(  #[prop(default = 100)]  max: u16,  #[prop(into)]  progress: Signal<i32>) -> impl IntoView {  view! {    <progress max=max value=progress />  }}

这个东西类似于面向对象中的装饰器,是给函数/属性提供额外能力的一种语法。uEU28资讯网——每日最新资讯28at.com

他有如下几种用法。uEU28资讯网——每日最新资讯28at.com

#[attribute="value"]#[attribute(key="value")]#[attribute(value)]

例如,我们将一个普通函数定义为一个组件,则对该函数使用如下的宏定义。uEU28资讯网——每日最新资讯28at.com

#[component]

接收一个参数 max,默认值为 100。uEU28资讯网——每日最新资讯28at.com

#[prop(default = 100)]max: u16,

支持任意类型的值传入,然后调用 .into() 去转化。uEU28资讯网——每日最新资讯28at.com

#[prop(into)]progress: Signal<i32>

因此,有了这个宏的帮助,我们的 progress 属性可以接收一个响应式属性,也可以接收一个普通属性。通过这种方式解决了 solid 在语法设计上面临的困境。uEU28资讯网——每日最新资讯28at.com

<ProgressBar progress=count /><ProgressBar progress=|| 100 />

三、总结

抛开 rust 的上手难度不谈,在语法设计上,Leptos 的语法设计我认为比 solid 要精妙得多。这是一种更成熟的语法构思。uEU28资讯网——每日最新资讯28at.com

但是响应式方案本身在语法上确实存在挑战,例如在 Solid 中还存在更严重的问题就是使用解构语法会导致数据失去响应性,因此最终也只能靠各种编译手段尽量抹平差异。但黑科技加多了,一不小心就在重新设计语法了。因此到目前为止,我依然更喜欢 React,他的语法设计足够简洁,编译手段的侵入性足够小,更符合 JavaScript 的语法逻辑。uEU28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-90668-0.html来自 Rust 生态的强烈冲击?谈谈 Leptos 在语法设计上的精妙之处

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Try-Catch的性能问题及其优化策略

下一篇: Vue3 中如何做高性能的拼音搜索,提高用户体验?

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • 红魔电竞平板评测:大屏幕硬实力

    前言:三年的疫情因为要上网课的原因激活了平板市场,如今网课的时代已经过去,大家的生活都恢复到了正轨,这也就意味着,真正考验平板电脑生存的环境来了。也就是面对着这种残酷的
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • 分享六款相见恨晚的PPT模版网站, 祝你做出精美的PPT!

    1、OfficePLUSOfficePLUS网站旨在为全球Office用户提供丰富的高品质原创PPT模板、实用文档、数据图表及个性化定制服务。优点:OfficePLUS是微软官方网站,囊括PPT模板、Word模
  • 共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • 质感不错!OPPO K11渲染图曝光:旗舰IMX890传感器首次下放

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
  • 北京:科技教育体验基地开始登记

      北京“科技馆之城”科技教育体验基地登记和认证工作日前启动。首批北京科技教育体验基地拟于2023年全国科普日期间挂牌,后续还将开展常态化登记。  北京科技教育体验基
Top