如果你是 React 技术栈,就会发现其对新手其实是不太友好的,会导致新人写出很多重复渲染的组件和 BUG,而且排查难度高(当然 React 依然是最优秀的框架,很多理念的提出者和先行者)。
当我看到 SolidJS[1] 后,我感觉这才是真的 react(响应式),它既包含了 React 的语法和天生的 TS 支持,又拥有比 Vue 还彻底的响应式设计,让你不用为 Deps
烦恼。
import { createSignal } from 'solid-js'const [count, setCount] = useState(0);createEffect(() => { console.log('count', count()) // 当 count 变化时,会自动触发}) // 不需要 deps复制代码
SolidJS 的优秀之处更多的是在于它的高性能,感兴趣的同学可以去其 官网[2] 了解一下。
综上,我个人很喜欢它的设计,也相信好的事物经过时间的发展最终会展露锋芒。
但现实情况是其生态很缺失,所以经过思考后和调研后,决定写一个工具函数库,一方面希望能参与到开源建设来,另一方面也希望能提高一下自己的能力,毕竟最好的学习技术方式就是马上用起来。
当然做开源也不仅是热爱,也有功利目的,希望能有一个国内外都认可的项目经验。
由于自己能力有限以及社区已经有很多优秀的项目,所以走的是搬运路线,而不是从 0 到 1 的创新。
在调研了 ahooks
、react-use
和 VueUse
等库后,决定采用 VueUse[3] 作为蓝本,后面经过 2 个月的业余时间开发,最终完成并开源了 solidjs-use[4]。
本文主要是记录一下在搬运过程中的部分技术实践和思考,希望对你的前端开发和开源有一些启发。
技术实践
Typescript 函数重载和联合类型
虽然 TS 也使用了很久但是有些东西还是傻傻不明白,其中就包含了错误的使用联合类型解决函数重载的问题。
当一个函数出入参数为 A
类型时,返回为 M
类型;输入参数为 B
类型时,返回为 N
类型。如果是以前我会这样写:
function foo(arg: A | B): M | N复制代码
但这样其实是不准的,因为我们明确知道 A
类型对应的是 M
类型,但是现在 A
类型却对应了 M | N
类型,这样我们在后续只能使用 any
或者 as
大法。
通过在搬砖的过程中,我对 TS 的函数重载有了正确的认知:不同参数个数或者不同参数类型,对应的返回值类型也不同时,需要使用函数重载,而不是联合类型。
function foo(arg: A): M;function foo(arg: B): N;function foo(arg: A | B) { // ...}复制代码
多包项目引用管理
在写 monorepo 项目时,a 项目引用 b 项目,当 b 项目发生改变,我们需要重新 build 出 b 产物,这样 A 拿到的才是变化后的 b 项目。
如果仅改一次还好,如果改多次 b 项目就需要启动 watch 模式,但如果 a 同时引入了 b 和 c 两个项目,那么就需要同时 watch 多个项目了,有点麻烦。
看了 VueUse 的源码,发现这个问题也很简单,只需要使用 resolve.alias
到依赖的 src 目录,而不是按照普通 node modules 的解析方式,就可以不用 watch 依赖了。
虽然这个点也很小,但是确确实实能提升开发幸福感。
PS:当然这个方案也有缺点,当依赖的源码过多,启动会变慢。
方法论思考
如何跨技术栈?
想要将 VueUse 转为 SolidJS 框架可能大部分人的想法是直接撸起袖子,对照着搬运就可以了(虽然最后的结果可能是这种方式)。
但当你把第一版拷贝过去,那么 VueUse 后续的更新怎么办?所以这其实并不是一个简单直白的问题,需要综合考虑到实现难度、后期可维护性。
我们假设需要将 Python 编译为 JS:
- 两者相同点有很多:例如 if/else、函数、循环等概念都是相通的
- 当然也有很多不同点,核心体现在:
- 语法层面:同样是函数,但 JS 中定义函数和 Python 定义函数写法是不同
- 运行时层面:Python 中的某个内置函数,JS 中没有对应功能的函数
针对不同点,有以下解决方案:
- 语法层面的不同:我们通过将 Python 代码结构化为 AST[5],然后修改及转化为 JS 的 AST 树,最后再将 JS AST 树转为 JS 代码
- 运行时层面:注入由 JS 实现的相同功能的函数即可
上面仅是说的 Python 转 JS,但在计算机中,涉及 2 个相似事物的转化,都是可以采用这套转化逻辑,其实就是编译原理的应用。
案例
前端最熟悉的 Babel 就是将最新的 JS 规范编译成老版本的 JS 规范,其中:
- 语法层面:将新语法转为老语法,例如
let
编译成var
- 运行时层面:使用 corse-js 完成 polyfill[6],例如 Promise 类
方案优缺点
优点:当写好了转换代码,则无论项目的多少或者大小,都能 cover
缺点:难度大,工作量大
solidjs-use 在最初设计的时候也是考虑过将 VueUse 通过语法转换的方式实现 solidjs-use,但由于部分逻辑实现难度大(太菜),而最终放弃。微信搜索公众号:架构师指南,回复:架构师 领取资料 。
抽象层是自己先制定一套规范/语法,然后在这套规范进行完成实际代码编写,最后通过编译,编译为其他的语言/框架。
本质上依然是方案1,但是要比方案 1 的难度低很多,因为在设计之初就考虑到不同框架之间的共性以及尽量避免其他框架无做到的事情。
暂无评论内容