2023-12-09 12:48:27 +00:00
|
|
|
|
---
|
|
|
|
|
title: Flutter, Dart和Signal范式
|
|
|
|
|
date: 2023-12-09 20:38:24
|
|
|
|
|
tags:
|
|
|
|
|
- Flutter
|
|
|
|
|
- Dart
|
|
|
|
|
- 前端开发
|
|
|
|
|
---
|
|
|
|
|
|
2023-12-09 13:04:41 +00:00
|
|
|
|
最近看到很多在Flutter上实现Signals范式的项目,其实我自己也试了一下。感想是:Dart缺少太多语法特性了……真正的Signals范式需要很多胶水代码,在JS上这些胶水代码都是用代码生成器生成的,但是Dart和Flutter让这个生成器不是那么的好写,或者没法方便的用自带的特性做类似的功能。
|
2023-12-09 12:48:27 +00:00
|
|
|
|
|
|
|
|
|
<!--more-->
|
|
|
|
|
|
|
|
|
|
比如说最关键的自动依赖跟踪,这是实现这个Signals的项目都有一大堆胶水代码的关键原因。举个JSX的例子:
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
<Hello name={isWorld() ? "world" : name}/>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
会被转译成类似下面的JS代码。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
createComponent(Hello, {
|
|
|
|
|
get name() {
|
|
|
|
|
return (isWorld() ? "world": name);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这样才能做到两个功能:
|
|
|
|
|
|
2023-12-09 13:04:41 +00:00
|
|
|
|
1. lazy evaluation。只有从props里面获取name时才会evaluate相应表达式;
|
|
|
|
|
2. 自动依赖跟踪。通过模拟一个dynamic-scope variable,lazy evaluation可以让signal在被访问时获得这个变量的值来跟踪依赖。这个是最简单而且计算最少的实现方法(时间复杂度可以做到常数级)。
|
2023-12-09 12:48:27 +00:00
|
|
|
|
|
|
|
|
|
但是在Dart和Flutter Widget里面,你很难处理成这样。
|
|
|
|
|
|
2023-12-09 13:04:41 +00:00
|
|
|
|
为了理解这个挑战,可以考虑一下下面这样的API怎么在Dart和Flutter上实现:
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
// 怎么恰当处理组件的类型?
|
|
|
|
|
const Hello: Component<{name: string, effectName: string}> = (props) => {
|
|
|
|
|
const [isWorld, setIsWorld] = createSignal(false);
|
|
|
|
|
|
|
|
|
|
createEffect(() => {
|
|
|
|
|
if (!isWorld()) {
|
|
|
|
|
console.log(props.name); // 如何跟踪这个依赖?
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createEffect(() => {
|
|
|
|
|
console.log(props.effectName); // 如何将这个的更新与上面那个区别开?
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return <div>
|
|
|
|
|
<p>Hello, {isWorld() ? "World" : name}</p> {/* 这个表达式该如何处理? */}
|
|
|
|
|
<button type="button" onClick={() => setIsWorld(true)}>Toggle</button>
|
|
|
|
|
</div>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default Hello;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果你通过InheritWidget这种来代替dynamic-scope variable做(依赖跟踪),它需要在Element树向上查找,这个性能损失太大了。Dart也没有好用的proxy范式,所以很难简单的实现lazy evaluation。如果你的参数收Signal对象,那也是胶水代码的重要来源。
|
2023-12-09 12:48:27 +00:00
|
|
|
|
|
|
|
|
|
Getx虽然很“脏”,但是它确实充分发展了Dart和Flutter提供的东西。
|
|
|
|
|
|
|
|
|
|
如果要在Flutter上实现Signal范式,我估计有几个是必须的:
|
|
|
|
|
|
|
|
|
|
- 计算类型
|
|
|
|
|
- inline object或者inline class
|
|
|
|
|
- 不再围绕Widget设计API(Signal系统可以提供的粒度其实比现在的Widget更小,它完全可以直接控制Element)
|