可视化搭建 - 自动批处理与冻结

news2025/1/13 10:08:26

性能在可视化搭建也是极为重要的,如何尽可能减少业务感知,最大程度的提升性能是关键。

其实声明式一定程度上可以说是牺牲了性能换来了可维护性,所以在一个完全声明式的框架下做性能优化还是非常有挑战的。我们采取了两种策略来优化性能,分别是自动批处理与冻结。

自动批处理

首先,框架内任何状态更新都不会立即触发响应,而是统一收集起来后,一次性触发响应,如下面的例子:

const divMeta: ComponentMeta = {
  // ...
  fetcher: ({ selector, setRuntimeProps, componentId }) => {
    const name = selector(({ props }) => props.name)
    const email = selector(({ props }) => props.email)
    fetch('...', {
      data: { name, email }
    }).then((res) => {
      setRuntimeProps(componentId, old => ({
        ...old ?? {},
        data: res.data
      }))
    })
  }
}

const App = () => {
  const { setProps } = useDesigner()
  const onClick = useCallback(() => {
    setProps('1', props => ({ ...props, name: 'bob' }))
    setProps('1', props => ({ ...props, email: '666@qq.com' }))
  }, [])
}

上面例子中,fetcher 通过 selector 监听了 props.nameprops.email,当连续调用两次 setProps 分别修改 props.nameprops.email 时,只会合并触发一次 fetcher 而不是两次,这种设计让业务代码减少了重复执行的次数,简化了业务逻辑复杂度。

另一方面,在自动批处理的背后,还有一个框架如何执行 selector 的性能优化点,即框架是否能感知到 fetcher 依赖了 props.nameprops.email?如果框架知道,那么当比如 props.appId 或者其他 state. 状态变化时,根本不需要执行 fetcher 内的 selector 判断返回引用是否变化,这能减少巨大的碎片化堆栈时间。

一个非常有效的收集方式是利用 Proxy,将 selector 内用到的数据代理化,利用代理监听哪些函数绑定了哪些变量,并在这些变量变化时按需重新执行。

笔者用一段较为结构化的文字描述这背后的性能优化是如何发生的。

一、组件元信息声明式依赖了某些值

比如下面的代码,在 meta.fetcher 利用 selector 获取了 props.nameprops.email 的值,并在这些值变化时重新执行 fetcher

const divMeta: ComponentMeta = {
  // ...
  fetcher: ({ selector, setRuntimeProps, componentId }) => {
    const name = selector(({ props }) => props.name)
    const email = selector(({ props }) => props.email)
  }
}

在这背后,其实 selector 内拿到的 props 或者 state 都已经是 Proxy 代理对象,框架内部会记录这些调用关系,比如这个例子中,会记录组件 ID 为 1 的组件,fetcher 绑定了 props.nameprops.email

二、状态变化

当任何地方触发了状态变化,都不会立刻计算,而是在 nextTick 时机触发清算。比如:

setProps('1', props => ({ ...props, name: 'bob' }))
setProps('1', props => ({ ...props, email: '666@qq.com' }))

虽然连续触发了两次 setProps,但框架内只会在 nextTick 时机总结出发生了一次变化,此时组件 ID 为 1 的组件实例 props.nameprops.email 发生了变化。

接着,会从内部 selector 依赖关系的缓存中找到,发现只有 fetcher 函数依赖了这两个值,所以就会精准的执行 fetcher 中两个 selector,执行结果发现相比之前的值引用变化了,最后判定需要重新执行 fetcher,至此响应式走完了一次流程。

当然在 fetcher 函数内可能再触发 setProps 等函数修改状态,此时会立刻进入判定循环直到所有循环走完。另外假设此次状态变化没有任何 meta 声明式函数依赖了,那么即便画布有上千个组件,每个组件实例绑定了十几个 meta 声明式函数,此时都不会触发任何一个函数的执行,性能不会随着画布组件增加而恶化。

冻结

冻结可以把组件的状态凝固,从而不再响应任何事件,也不会重新渲染。

const chart: ComponentMeta = {
  /** 默认 false */,
  defaultFreeze: true
}

或者使用 setFreeze 修改冻结状态:

const { setFreeze } = useDesigner()
// 设置 id 1 的组件为冻结态
setFreeze('1', true)

为什么要提供冻结能力?

当仪表盘内组件数量过多时,业务上会考虑做按需加载,或者按需查询。但因为组件间存在关联关系,可视化搭建框架(我们用 Designer 指代)在初始化依然会执行一些初始函数,比如 init,同时组件依然会进行一次初始化渲染,虽然业务层会做一些简化处理,比如提前 Return null, 但组件数量多了之后想要扣性能依然还有优化空间。

所以 Designer 就提供了冻结能力,从根本上解决视窗外组件造成的性能影响。为什么可以根本解决性能影响呢?因为处于冻结态的组件:

  • 前置性。通过 defaultFreeze 在组件元信息初始化设置为 false,那么所有初始化逻辑都不会执行。

  • 不会响应任何状态变更,连内置的 selector 执行都会直接跳过,完全屏蔽了这个组件的存在,可以让 Designer 内部调度逻辑大大提效。

  • 不会触发重渲染。如果组件初始化就设置为冻结,那么初始化渲染也不会执行。

怎么使用冻结能力?

建议统一把所有组件 defaultFreeze 设置为 true,然后找一个地方监听滚动或者视窗的变化,通过 setFreeze 响应式的把视窗内组件解冻,把移除视窗的组件冻结。

特别注意,如果有组件联动,冻结了触发组件会导致联动失效,因此业务最好把那些 即便不在视窗内,也要作用联动 的组件保持解冻状态。

总结

总结一下,首先因为声明式代码中修改状态的地方很分散,甚至执行时机都交由框架内部控制,因此手动 batch 肯定是不可行的,基于此得到了更方便,性能全方面优化了的自动 batch。

其次是业务层面的优化,当组件在视窗外后,对其所有响应监听都可以停止,所以我们想到定义出冻结的概念,让业务自行决定哪些组件处于冻结态,同时冻结的组件从元信息的所有回调函数,到渲染都会完全停止,可以说,画布即便存在一万个冻结状态的组件,也仅仅只有内存消耗,完全可以做到 0 CPU 消耗。

讨论地址是:精读《自动批处理与冻结》· Issue #484 · dt-fe/weekly

如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

741e754e28af0d35a46723223239482d.jpeg

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/558005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我的创作纪念日,成为创作者的第256天!

机缘 一年前刚开始学习编程,在网上查找资料的过程中才了解到了CSDN开发者社区。在csdn认识了很多技术大牛,他们的文章记录了他们的学习路径,看到他们从小白一步一步成长为大牛,这激起了我创作的热情。刚开始写博客完全是日常学习…

广域通信网 - HDLC 高级数据链路控制协议

文章目录 1 概述2 HDLC2.1 帧类型2.2 帧结构 3 扩展3.1 网工软考真题 1 概述 #mermaid-svg-JEuFH1qP4tY5jI5p {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JEuFH1qP4tY5jI5p .error-icon{fill:#552222;}#mermaid-…

快看!ChatGPT的4个不为人知却非常实用的小功能

文 / 高扬(微信公众号:量子论) 今天重点介绍四个ChatGPT很实用的小功能。 一、停止生成 如果在ChatGPT输出内容的过程中,我们发现结果不是自己想要的,可以直接点击“Stop generating”按钮,这样它就会立即停…

小鱼说|城市产业带与供应链系统的结合模式(2)

上一篇讲到城市产业带 的形成以及讲到 它与供应链的关系 那么我们继续把 这个话题深化一下 从原厂地到供应商 到供应链再到商城平台 城市产业带是供货的源地 经过最多二级的供应商上架 到供应链再经过最多二级的 经销商流入到各大商业平台 由于一个供应链可对接多个城市产业带 …

公网远程访问本地jupyter notebook服务 - 内网穿透

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自cpolar的文章:公网远程访问Jupyter Notebook【Cpolar内网穿透】 前言 Jupyter Notebook&am…

ChatGPT为一位英语女教师做了这件事

文 / 高扬(微信公众号:量子论) 今天有位网友加我微信,是某中学的英语老师,通过“量子论”公众号知晓了ChatGPT的一些有趣玩法,询问是否有办法帮她做个提示词,依据一个单词生成配套学习资料。 即…

代码实战深度理解RabbitMQ 5 种消息模型

5种消息模式 简单消息模式:1个生产者 1个队列 1个消费者;生产者只负责生产,消费者只负责消费,两者在同一个队列中操作工作队列消息模式:1个生产者 1个队列 多个消费者; 一条消息只能被消费一次订阅消息…

AI作曲都这么厉害了,AI生成音乐

人工智能(AI)正在越来越多地应用于音乐、电影和绘画等艺术领域。在之前海森大数据已经为大家介绍了AI生成绘画,今天带大家了解一下AI生成音乐。 在当下的应用中,AI音乐生成已经相对成熟,AI已经可以影响音乐制作过程的…

annoconda安装使用及镜像源的添加,提高软件下载速度

1 annoconda下载 文件地址列表,选择版本下载https://repo.anaconda.com/archive/ win10版本: Anaconda3-2023.03-1-Windows-x86_64 linux版本: Anaconda3-2023.03-1-Linux-x86_64 win10下执行exe按向导安装,linu…

人工智能和物联网:如何将传感器和设备数据与机器学习相结合

第一章:引言 人工智能(Artificial Intelligence, AI)和物联网(Internet of Things, IoT)是当今科技领域最引人注目的技术之一。随着传感器和设备的普及,我们能够收集到大量的实时数据。然而,这…

chatgpt赋能Python-python_head__

Python的head()方法 什么是head()方法? head()方法是Python编程语言中的一个函数,它用于获取一个序列的前几项。它的用法如下: head(n, iterable)其中,n表示需要返回的序列前n项,iterable表示需要获取前n项的序列对…

2023年海彼特全国幼儿篮球联赛·总决赛圆满落幕

5月21日,由北京海彼特教育科技院主办的“2023年海彼特全国幼儿篮球联赛总决赛”。在河北体育馆隆重举行,精彩的比赛效果使体育馆顿时成为幼儿篮球界最高端、大气的舞台。 本次盛会联合举办方有: 河北体育馆 亚洲少儿体育协会 北京海彼特文…

【Linux】signal 和 sigaction 两个信号捕捉函数

目录 signal 信号捕捉函数1、函数解析2、代码示例 sigaction 信号捕捉函数1、函数解析2、代码示例 内核实现信号捕捉的过程 sigaction的用法要复杂一些,但一般都是用sigaction,signal依据不同的标准可能有不同的用法变化,sigaction比较稳定&a…

易基因:全基因组DNA甲基化分析揭示DNMT1在斑马鱼模型听觉系统发育中的作用 | 胚胎发育

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 听力障碍通常与内耳发育不全或损伤有关,是影响生活质量的严重健康问题。因此研究听觉器官发生过程中的关键基因对于探索听力损伤的潜在策略至关重要。斑马鱼模型在理解内耳发…

C++ Primer笔记——查找算法

目录 一.简单查找 ①find(first, last, val); ②find_if & find_if_not ③count & count_if ④all_of & any_of & none_of 二.重复值的查找 ①adjacent_find(first, end); ②search_n(first, end, count, val); 三.查找子序列 ①search(first1, end1,…

基于html+css的图展示86

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Redis事务和Redis管道

什么是Redis事务:Redis事务是指将多条命令加入到队列里面,一次批量执行多条命令,每一条命令会按顺序执行,在事务执行过程中不会受到客户端所传入的命令请求的影响 1)单独的隔离操作:Redis的事务仅仅保证事务 里面的操作会被连续独占的执行&am…

【Rust 日报】2023-05-21 Helix 23.05发布

Helix 23.05发布 Helix 是个文本编辑器: 新版本功能: 为 LSP 引用请求添加一个配置选项,用于排除声明(#6886)。根据文件扩展名和 shebang 启用语言注入(#3970)。通过最近…

Vision-CAIR/MiniGPT-4:使用先进的大型语言模型增强视觉-语言理解

Vision-CAIR/MiniGPT-4:使用先进的大型语言模型增强视觉-语言理解 摘要 视觉-语言理解是人工智能领域的一个重要方向,它涉及到图像和文本之间的复杂交互。近年来,大型语言模型(LLM)在自然语言处理(NLP&am…

【PCIE732】基于 Kintex UltraScale 系列FPGA 的2 路40G 光纤通道适配器(5GByte/s 带宽)/XCKU060

板卡概述 PCIE732 是一款基于PCIE 总线架构的高性能数据传输卡,板卡具有1 个PCIex8 主机接口、2 个QSFP40G 光纤接口,可以实现2路QSFP 40G 光纤的数据实时采集、传输。板卡采用Xilinx 的高性能Kintex UltraScale 系列FPGA 作为实时处理器,板…