前端性能:股票交易APP频繁更新怎么破

写本文的原因
  • 有几位小伙伴最近又来问这个问题,之前帮人解答过一次,今天写下来

  • 以后有时间会多写一些解决方案,例如oom了,不用esbuild怎么解决之类的等..

正式开始

主题:股票交易APP(IM场景前端交互高频更新卡顿)
  • 一个正常的股票交易APP,是很复杂的,大都用原生写,但是有的公司没钱啊,只能做一套web app或者用RN这些写,也有用Flutter的(这就是没钱又要玩,那怎么办呢?那就玩乞丐版呀)

一个股票交易APP的界面长这样
问题重现
  • 用户收藏了1000只自选股(国内国外+期货+指数等),技术栈是web app ,基于react或React-native,很卡顿

  • 由于是双工通讯,而且高频推送,触发更新,而且交易类APP对消息送达的效率/低延迟要求非常高,例如你准备买这只股票,此时大户砸盘,你还没收到更新的信息,下单,发现趋势已经走坏,然后接盘被套。

  • 还有一种情况,你买入的时候出了大利好,你下单价格是10块钱,但是此时已经涨到10.05,这个价格成成交不了,然后你错过了一波大涨。这时候客户就惨了

需求简单&技术的剖析
  • 理论上用户可以添加的自选股票,是无限的

  • 每个自选股/股票的都有对应的事件触发

  • 高频更新,此时要区分react/react-native环境,因为react-native组件在挂载后就不会卸载了,不像web app.

原则
  • 性能优化最好是简单的手段

  • 所见即所得,简单高校,不触碰底层逻辑,例如网络层前后端可能都要做粘包的处理

  • ...不做可能诱发P0级别事故的技术方向选择

解决问题
  • react/react-native渲染上有区别,对于长列表,react-native是有组件对应只渲染可视区域,react则不会,需要虚拟列表,推荐react-peter-window这个库,而且可以支持自动高宽

源码demo地址:https://github.com/JinJieTan/react-keepAlive-dynamic
  • 这样react也可以跟react-native的组件一样,只渲染可视区域了

  • 长列表问题解决了,但是事件同时也很麻烦,理论上用户可以添加无限的自选股,这个列表可能就有无限长(不要说不可能,世界在发展,这就是高可用的APP),传统的事件需要每个item去绑定,然后切换组件时候再remove掉,但是频繁对事件挂载、移除其实也很损耗性能,这里换成事件冒泡,就可以了,把需要的数据挂载到dom的属性上获取即可~

  • 上面说的,不要小看,能解决相当一部分性能问题

最重要的高频更新的问题
  • 不同金融交易类公司,后端架构设计不一样,消息推送也是,例如大智慧的后端架构就比较特殊.

  • 前端网络层可能要处理粘包,后端的消息推送频率我们不管

  • 借鉴PReact、Redis、kafka的思想,自己在前端实现一个消息队列,定期消费,更新界面.

  • 参考我之前手写React的代码:

https://github.com/JinJieTan/mini-react/tree/hooks

import { _render } from '../reactDom/index';

import { enqueueSetState } from './setState';

export class Component {

  constuctor(props = {}) {
    this.state = {};
    this.props = props;
  }
  
  setState(stateChange) {
    const newState = Object.assign(this.state || {}, stateChange);
    console.log(newState,'newState')
    this.newState = newState;
    enqueueSetState(newState, this);
  }
  
}

  • 当setState后,先进入队列中,首次进入,队列为空,进入判断,下一帧渲染前调用defer(flush)

export function enqueueSetState(stateChange, component) {

  //第一次进来肯定会先调用defer函数
  if (setStateQueue.length === 0) {
    //清空队列的办法是异步执行,下面都是同步执行的一些计算
    defer(flush);
  }

  //向队列中添加对象 key:stateChange value:component
  setStateQueue.push({
    stateChange,
    component,
  });

  //如果渲染队列中没有这个组件 那么添加进去
  if (!renderQueue.some((item) => item === component)) {
    renderQueue.push(component);
  }
}
  • defer函数

function defer(fn) {
  //高优先级任务 异步的 先挂起
  return requestAnimationFrame(fn);
}
  • 此时消息再次推送,再次触发enqueueSetState,数据此时被推送到队列中,一帧统一合并消费。

其实浏览器也是有渲染队列的,例如你在一个for循环里面频繁操作dom,并不会每次操作dom都会导致浏览器渲染,达到一个阀值,就会触发渲染,当然你也可以手动控制清空队列(这里不写太深,有兴趣的可以关注后面的文章)

写在最后

  • 我是Peter,架构设计过20万人端到端加密超级群功能的桌面IM软件,现在是一名前端架构师。

  • 如果你对性能优化有很深的研究,可以跟我一起交流交流,今天这里写得比较浅,但是大部分人都够用,之前问我的朋友,我让它写了一个定时器定时消费队列,最后也能用。哈哈

  • 另外欢迎收藏我的资料网站:前端生活社区:https://qianduan.life

  • 感觉对你有帮助,可以右下角点个在看,关注一波公众号:[前端巅峰]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值