网站首页 > 开源技术 正文
大家好,我卡颂。
最近,Angular、Qwik的作者「MI?KO HEVERY」发文表示Signal是前端框架的未来,并考虑在Angular中实现它。
在此之前,Vue、Solid.js、Preact、Svelte都已实现Signal。实际上,signal并不是一个新概念,他还有很多别名,比如:
响应式更新
细粒度更新
如果你了解过Vue响应式更新的实现原理,对Signal就不会陌生。
实际上,Signal的技术在10年前Knockout框架中就有应用。为什么这项技术正受到越来越多前端框架的青睐?
本文,让我们一起探讨下这个话题。
signal的本质
signal的本质,是将「对状态的引用」以及「对状态值的获取」分离开。这么说可能有点抽象,让我们先看一个非signal的例子。
以下是React中定义状态的方式:
function App() { const [state, dispatch] = useState(0); return <p onClick={ () => dispatch(state + 1) }>{state}</p> }
useState的返回值包括两部分:
state:状态的值
dispatch:状态的setter
可以发现,state耦合了「对状态的引用」以及「对状态值的获取」这两个含义。
再来看一个signal的例子。以下是同一个例子用Solid.js书写的样子:
function App() { const [getState, dispatch] = createSignal(0); return <p onClick={ () => dispatch(getState() + 1) }>{getState()}</p> }
createSignal的返回值包括两部分:
getState:对状态的引用
dispatch:状态的setter
区别就体现在getState上,其中:
getState是对状态的引用
getState()是对状态值的获取
也就是说,我们可以不必立刻获取状态的值,而是在需要的时候再获取(即在需要时再执行getState())。
这么做有什么好处呢?如果我们在需要的时候再获取状态的值,就能感知当前的上下文环境。
举个很粗糙的例子,在下面的代码中,组件实例(Component实例)在render时会将全局变量cpnContext指向自己:
let cpnContext = null; class Component { render() { cpnContext = this; // ...省略逻辑 } }
那么在createSignal返回的getState方法内部,可以获取全局变量cpnContext来感知当前处于哪个组件的渲染流程:
function createSignal() { // ...省略逻辑 function getState() { const curContext = cpnContext; // ... } function dispatch {} return [getState, dispatch] }
这么做的目的是建立「状态变化」与「需要更新哪个组件」之间的联系。
相比于React,基于Signal实现的框架会有两个优势:
更好的细粒度更新性能
更好的DX(开发者体验)
更好的细粒度更新性能
由于Signal建立了状态与组件之间的联系,所以相比于React更有性能优势。
比如,在我的电脑上,用Svelte渲染1w个li,点击某个li后改变他的内容:
<ul> {#each items as item (item.id)} <li on:click={() => items[item.id].name = 'change!'}>{item.name}</li> {/each} </ul>
从点击事件触发,到Svelte逻辑运行完,再到浏览器重排重绘,总用时18.88ms,其中Svelte的逻辑执行只花了9.5ms:
同样的例子用React实现,触发点击后总用时98.5ms,其中React的逻辑执行了89.38ms:
在这个例子中,React性能比Svelte差了一个数量级。之所以会有这样的差异,很大一部分原因在于「Svlete在更新前就知道状态变化时需要更新哪个组件」。
而这一切的源头就在于Signal。
更好的DX
更好的开发者体验主要体现在两方面:
Signal感知上下文环境的能力减少了代码心智负担
比如在React中,useEffect在使用时需要指明依赖的状态:
useEffect(() => { // ...state1, state2变化后的逻辑 }, [state1, state2])
如果采用Signal的实现,状态能感知到自己在useEffect上下文环境,可以自动建立两者之间的联系,不用再担心少写依赖状态、闭包陷阱等问题,减少心智负担。
比如在Vue中,类似useEffect(仅仅是功能类似,两者的用途其实是不同的)的watch,就不需要显式指明依赖:
<script setup> import { ref, watch } from 'vue' const name = ref('') watch(name, (newName, oldName) => { // ...省略逻辑 }) </script>
减少开发者性能优化的心智负担
使用Signal的框架通常能获得不错的运行时性能,所以不需要额外的性能优化API。反观React,开发者如果遇到性能问题,需要手动调用性能优化API(比如React.memo、useMemo、PureComponent)。
总结
有以上这么多优点,难怪很多框架都使用了Signal。那么React对Signal是什么态度呢?
React团队成员对此的观点是:
有可能引入类似Signal的原语
Signal性能确实好,但他不太符合React的理念
React的理念可以用一句话概括:「UI反映状态在某一刻的快照」。
既然是快照,那就不是局部的,而是个整体概念。在React中,状态更新会引起整个应用重新render,就是对React快照理念的最好诠释。
React现阶段的所有实现都是基于快照理念。所以,即使引入类似Signal的原语,可能也是类似Mobx这样的上层实现,而不是从底层重构。
我个人比较倾向于认为:React团队承认Signal的优点,但由于积重难返,而且现代设备的性能通常是过剩的,所以性能问题并不是首要问题。
如果这个观点是正确的,那么React可能会在开发者体验(Signal的另一个优点)方面努努力。比如去年提出的RFC: useEvent可能就是这方面的一次尝试。
猜你喜欢
- 2024-12-17 React 正在成为全栈框架 react框架优缺点
- 2024-12-17 听说后端的你在学 React 后端到底要学什么
- 2024-12-17 Rich Harris :Svelte 5.0 可减少代码编写量
- 2024-12-17 在w3cschool上学完html、css后要怎么提升
- 2024-12-17 连载:2016年最好的JS框架和库(上)
- 2024-12-17 2021年Top 5主流用户界面(UI)控件推荐
- 2024-12-17 连载:前端开发中纠结的Javascript框架(上)
- 2024-12-17 12条专业的js规则 js必须掌握的
- 2024-12-17 国货之光?用Rust编写的Vivo Blue OS
- 2024-12-17 knockout+echarts实现图表展示 echarts 地图可视化
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)