前言
2018年,我接触了 React。并且成为了工作中最主要的技术栈。
从 React16 开始到 React18 ,从类组件到函数式组件,从新手到熟悉各个 api 的特性。
虽然使用 React 解决了很多问题,
减少了工程的复杂度
缩短了 js 到 html 的编码距离(工程师的人机交互)
但是还有很多不足。
响应式(Reactive)
现在前端编程库都会拥有响应式的功能,但为什么响应式解决的是工程的复杂度问题?
使用响应式库前
代码围绕 Dom 来编写。
当工程规模变大时,Dom Api 代码会越来越多。自然出现了按照 Dom Api 修改了哪部分 Dom 来进行模块划分的方式。
数据应该放在哪里?
谁负责操作 Dom ?
又会因为一些新的问题,又引入面相对象的思路
使用响应式库后
代码围绕数据 => 视图的关系来编写。即 View = f(Data) 的表示。
数据与视图操作不再耦合,代替的是关系与视图的耦合。
数据可以根据场景,自由组织且不受设计模式与原则限制。
响应式库根据关系与视图的耦合代码操作 Dom,100% 代替了以操作 Dom 为目的的代码。
小结
代码量减少,编码自由度提高,减少设计模式的限制。必然会降低工程复杂度。
JSX 与 HTML 模板
HTML 模板
JSX 与 HTML 模板是减少 Dom Api 代码的有效手段。
HTML 模板在响应式编程以前就已经存在,如 PUG EJS 等。
然而只使用模板并不能解决工程上的问题,仍然需要 Dom Api 来辅助完成工程师的意图。
在引入响应式编程后,有效的降低了工程复杂度问题,使 HTML 模板变为一种强力工具。
JSX
JSX 在此方面更加激进,它允许 js 与 tag 混合的方式来描述数据与视图的关系。
因此,在编码时(人机交互的一种)工程师可以更加轻松的看到数据与视图的关系。
小结
JSX 与 HTML 模板是两种不同的编程风格,但是他们解决问题相同。
HTML 模板并没有形成与库无关的语法定义。
JSX 已经被内置到了各种编辑器的默认支持中。
React 的问题
提取用户代码意图的思路
const [state, setState] = useState([1, 2, 3]);
const A = () => createElement("p");
const Root = () =>
createElement("div", {
children: state.map((id) => {
createElement(A, {}, id);
}),
});
render(createElement(Root));
在 JS 脚本中,Root 的 children 始终会在 createElement("div") 执行之前被计算出结果。
此处 数据与视图 关系的被 createElement(Root) 执行时所创建出的作用域保存。
所以 Root 函数会在 state 产生变化时重新执行。也因此带来了一些问题。
- 函数重新执行可能带有副作用(side effect)。
数据与视图关系没有发生变化,但是函数重新执行。
过低的下限
在不使用经过特殊优化的函数时 (如 solidjs flow_for),响应式库无法依据代码意图精准操作 Dom。
为了解决上面的问题,React 使用了以下的解决方案。
- 使用 Virtual Dom 的来计算发生变化的 Dom,以此来精确操作Dom。同时避免
数据与视图关系未发生变化的部分产生 Dom 变化。 - 要求包含副作用的用户代码放置于 effect 中,并使用 deps 对比的方案来决定是否要重新执行 effect
也因为这些可选API,导致了低代码质量的 React 工程诞生。其根本原因是 React 提取用户代码意图的思路
替代品
github.com/idealjs/sap… 项目仍在施工中。
sapling 源自于2022年时看到的 solidjs 文章。
2023 年再一次受到世界上最小的响应式 UI 库 vanjs 的启发。
期望新的 提取用户代码意图的思路 可以带来更好的性能。