[Flutter小试牛刀] 低配版signals,添加多层监听链

本文续接上篇文章: #[Flutter小试牛刀] 写一个低配版的signals

在上篇文章里,我们已经写了一个减配版的signals,但它仍然有很多缺陷:

  1. 节点的改变监听应该自己维护,当获取自己的值时可以重置监听链。
  2. 它不支持多层监听链,computed里面只能引用use,用起来就很不方便。

我们先分析一下运用signals的代码:

dart 复制代码
bool flag = true;
final a = signal(0); 
final b = signal(1); 
final c = computed(() => a.value + b.value); 
final d = computed((){
    if(flag) return c.value +b.value;
    return b.value;
); 
final dispose = effect((){ print("d is ${d.value}"); }); 

expect(c.value, 1); 
expect(d.value, 2); 
a.value = 2; 
expect(c.value, 3); 
expect(d.value, 4); 
flag = false;
expect(c.value, 3); 
expect(d.value, 1); 
dispose();

从上面代码中可以看出 d监听了c值的改变,而c值也是算出来的,这就是需要我们的框架支持多层监听链。 我们还可以看到 flag的值决定了d变量的监听对象,如果flag = true,那么d需要监听c和b的值改变,如果flag = false,则只需要监听b的值改变。这就需要我们在每次调用d.value都需要重新建立它的监听体系。

为了解决问题1和问题2,我们对代码进行以下改造:

dart 复制代码
abstract mixin class Node {

    final Set<Node> children = {};
    
    
    //将自己的值改变绑定到节点
    void _subscribeToNode(Node node) {
        if (children.contains(node)) {
            return;
        }
        addListener(node.notifyListeners);
        node.children.add(this);
    }
    //取消将自己的值改变绑定到节点
    void unSubscribeToNode(Node node) {
        if (children.contains(node)) {
            node.removeListener(notifyListeners);
            children.remove(node);
        }
    }
    //清除所有监听
    void _unsubscribe() {
        for (var child in children) {
        child.removeListener(notifyListeners);

        }
        children.clear();
    }

    void addListener(void Function() listener);
    void removeListener(void Function() listener);
    void dispose();
    void notifyListeners();
   
}

class UseCompute<T> with Node {
    DisposeFn<T>? onDispose;
    ChangeFn<T>? onChange;
    final CreateFn<T> onCreate;
    final Set<void Function()> _listeners = {};
    T? _value;
    UseCompute(this.onCreate);
    
    T? get value {
        //如果全局节点不为空将自己的改变绑定到全局节点。
        if (_currentNode != null) {
            _subscribeToNode(_currentNode!);
        }
        //清除之前的监听节点列表。
        _unsubscribe();
        //缓存全局节点
        var oldCurrentNode = _currentNode;
        try {
        //将自己设置成全局节点
            _currentNode = this;
            //重新建立监听节点列表。
            var newValue = onCreate.call();
            //如果值有改变则回调
            if (_value != newValue) {
                onChange?.call(newValue);
            }
            _value = newValue;
            return _value;
        } catch (error) {
            rethrow;
        } finally {
            //将全局节点重置成之前的缓存值
            _currentNode = oldCurrentNode;
        }
    }
    //不添加监听获取值
    T? peek() {
        return _value;
    }
    
    @override
    void addListener(void Function() listener) {
        _listeners.add(listener);
    }

    @override
    void removeListener(void Function() listener) {
        _listeners.remove(listener);
    }

    @override
    void dispose() {
        _listeners.clear();
        _unsubscribe();
        onDispose?.call(_value);
        _value = null;
    }

    @override
    void notifyListeners() {
        for (var listener in _listeners) {
        listener.call();
    }
}

class UseCreate<T> extends UseCompute<T> {

    UseCreate(super.onCreate);
    
    @override
    T? get value {
        //如果全局节点不为空则将自己的改变绑定到全局节点。
        if (_currentNode != null) {
            _subscribeToNode(_currentNode!);
        }
        //如果值为空则尝试初始化它
        _value ??= onCreate.call();
        return _value;
    }

    set value(T? newValue) {
        if (_value == newValue) {
            return;
        }
            _value = newValue;
            notifyListeners();
            onChange?.call(_value);
        }
    }
}

整个改造,UseCompute的 get value方法是核心。他们核心步骤是:

1.如果全局节点不为空则自己的改变事件绑定到全局节点。 2.清除之前的监听节点列表。 3.缓存全局节点,将自己设置成全局节点。 4.重新建立监听节点列表。 5.将全局节点重置成之前的缓存值

自此所有改造已经完成

相关推荐
还有你Y36 分钟前
Shell 脚本语法
前端·语法·sh
踩着两条虫2 小时前
如何评价VTJ.PRO?
前端·架构·ai编程
Mh3 小时前
鼠标跟随倾斜动效
前端·css·vue.js
小码哥_常4 小时前
Kotlin类型魔法:Any、Unit、Nothing 深度探秘
前端
冬奇Lab5 小时前
Android 开发要变天了:Google 专为 Agent 重建工具链,Token 减少 70%、速度提升 3 倍
android·人工智能·ai编程
Web极客码5 小时前
深入了解WordPress网站访客意图
服务器·前端·wordpress
幺风6 小时前
Claude Code 源码分析 — Tool/MCP/Skill 可扩展工具系统
前端·javascript·ai编程
vjmap6 小时前
唯杰地图CAD图层加高性能特效扩展包发布
前端·gis
ZC跨境爬虫6 小时前
3D 地球卫星轨道可视化平台开发 Day7(AI异步加速+卫星系列精简+AI Agent自动评论)
前端·人工智能·3d·html·json
ID_180079054736 小时前
淘宝 API 上货 / 商品搬家 业务场景实现 + JSON 返回示例
前端·javascript·json