聊聊Flare应用前后端性能优化问题

目录
  • 写在前面
  • 应用性能问题分析
    • 对于我不适用的功能
    • 前端架构中的问题
    • 后端架构中的问题
  • 针对应用进行改进
    • 调整前端实现
    • 调整后端实现
    • 图标资源优化
    • 容器镜像的优化
  • 额外的优化
  • 最后

两周前,在给颜值在线的 flame 提交了几个 PR 之后,我将它封装成了容器,用于书签和在线应用的管理。

但是在迁移个人书签的过程中,我发觉 flame 在性能上的表现并不是特别好,于是我做了一个改良版:flare

写在前面

在聊 flare 之前,我想先聊聊 flame。

flame 是一个颜值在线的导航页项目,你可以将你常用的书签和在线应用存储在它上面。它由一个波兰小哥创建,项目地址是 https://github.com/pawelmalak/flame

Flame 默认界面

在试用之后,我觉得项目还不错,于是稍作调整,封装了一个新的镜像:https://github.com/soulteary/docker-flame

新封装的应用

在项目文档中,记录了我的修改:

  • 简化程序功能和依赖(如K8S),减少软件包体积,重构了一些细节逻辑,简化应用启动流程。
  • 重写了天气获取逻辑,使用城市名称替换经纬度来获取天气数据。
  • 对程序已有的一些小 BUG 进行了修复,支持中文搜索。
  • 对程序进行了简单的汉化。

但是随着深入使用,我发现页面有着比较大的性能问题。

因为波兰小哥采用了 SPA 方案,项目内又包含了 6700 多个 SVG 图标、以及若干网页字体,并且在接口上有一些小问题,导致了页面体积非常大,接口请求数量也非常多。

甚至当你上传一些包含元素比较多的 SVG 作为你书签图标的时候,由 React 触发的页面渲染会造成浏览器卡死。

我大概有几百个书签需要处理,预估未来书签数量还会增长,所以我使用程序批量创建了接近一千个书签。然后我发现渲染如此多的书签,页面会出现卡顿,甚至在页面内搜索包含关键词的书签也会感受到明显的掉帧。

所以,我决定重新写一个轻量一些的程序,来解决我的需求。新的项目地址在这里,如果你好奇的话,可以试试看:https://github.com/soulteary/docker-flare

制作 flare 的过程,其实也是 flame 性能调优的过程。不过在解决问题之前,我们首先得能定位问题有哪些。

应用性能问题分析

关于这个应用的性能优化,其实并不复杂,和传统应用优化差别不大:优先减少计算量,在实在减少不了的情况下使用计算效率更高的方式来解决问题。

不过结合使用场景来说,在分析技术问题之前,可以先从功能入手。

对于我不适用的功能

首先从功能上看,我不需要这个应用与 Docker 集成,提供“服务发现”功能。比如在我启动容器后,这个应用会自动将新启动的容器作为书签或者应用进行添加。

其次,在拥有自己的 SSO 服务之后,我也不再需要使用简单的账号密码登录之类的功能,所以这个功能也可以去掉。

最后,关于书签数据的存储,我觉得在缺少用户体验非常棒的 Web 编辑器的前提下,可能不如配置声明的方式更易于管理和维护。(你可以使用任何你喜欢的编辑器来更新和维护内容,你可以使用 Git 或者任何你喜欢的方式,以白盒形式保存你自己的数据)。

基于上面的变化,我大概可以少写几个部分的代码:容器(Docker & K8S)集成、登录鉴权、应用和书签,以及书签分类的 “CRUD”。

前端架构中的问题

Flame 项目中,作者使用都是 create-react-app 脚手架创建的项目,项目依赖为: React v17 + TypeScript + Redux,为了提供简洁一致的图标,作者在前端引入了 Templarian/MaterialDesign-JS,一个被精心处理过的 DOM 结构非常简单的 SVG 图标库。

在使用构建工具打包、服务端 GZip 压缩之后,需要传输接近 1MB 的资源,原始脚本程序体积接近 3MB。相对膨大的程序体积导致了页面加载和执行时间都会比较长。比如页面页面首次渲染时间在 1s 上下浮动,多数情况下会超过 1s,完成时间一般都在 1.5s 以上。可能是作者对于服务端程序开发不够熟悉,虽然在前端进行应用配置更新时会复用接口,但是在内容页面展示时,会调用多达8个接口。此外,为了在页面中展示和更新天气信息,波兰小哥还使用了 WebSocket 来进行数据交互。

Flame 应用性能概览

其他的问题,在文章前面已经提到过了,就不赘述了。

后端架构中的问题

项目使用的技术栈为 Node.js,Web 框架为市占率非常高的 Express 的最新版本,ORM 框架选择的则是 Sequelize,数据存储落地为 SQLite3 。上面的选择粗看问题不大,如果应用不需要公开提供浏览访问,应该不会出现任何性能问题。

但是,如果我们仔细观察服务响应,会发现有一些请求的响应的时间非常长,比如页面资源、比如对于页面至关重要的 JS 程序资源,它们的获取都消耗了接近 400ms。

Flame 网络请求记录

此外,前端发起了多次请求来获取数据,结合数据存储使用 SQLite,如果提供公开内容访问,很容易遇到性能瓶颈。

针对应用进行改进

当我们清楚了解到上面的问题之后,比如容易采取的方案是:基于原程序进行重构调整,简化前端请求、合理拆分模块、处理资源加载和执行时机,调整数据存储和处理方式,提高服务端响应能力等组合拳。然而,这会是最简单和收益最高的方案嘛

调整前端实现

如果说在需要交互的页面程序中使用 MVVM 框架会有较高的收益和性价比,那么在缺少多端组件代码复用、没有服务端渲染需求的场景下,使用这类框架则是一个性价比不高的选择。

或许有同学会问,如果不使用 React、Vue、Angular 这类框架,难道在 2022 年还要再拾起 jQuery 等老的工具吗?虽然可以,但其实在近乎于纯展示的场景下,我们可以脱离 JS 来实现业务功能和简单的交互,比如自动获取焦点、菜单按钮的激活状态变化、甚至是带有动画效果的天气图标。

所以,在调整实现的时候,其实还有一个选择:不使用任何脚本

Flare 无脚本实现的渲染效率

在完成程序之后,我们可以看到从服务器获取整个页面数据、结构解析、样式计算、元素布局、页面绘制的完整时间在 33ms(包含了 idle 等待时间),其中关键流程的时间消耗加起来不到 10ms,而完成页面渲染的时间更是缩短到了 1.65ms。

在得到了页面快速渲染能力之后,即使不使用浏览器针对资源进行缓存,加速渲染,我们也能够做到页面切换的“无刷新”浏览(因为渲染速度足够快)。

调整后端实现

虽然我非常喜欢使用 Node.js,以往也分享过你所未知的3种Node.js代码优化方式,但是,为了能够低成本提高高性能的资源响应,这里进行技术栈切换是必要的:比如 Golang。

在使用 Golang 简化程序功能后,程序对于每个请求的响应基本能够保持在几毫秒的水平(受限于网络传输),相比较之前大概下降了2~3个量级。页面关键的 DOM ContentLoad 时间更是缩短到原来的八分之一。

Flare 优化过后批量请求状况

结合上面的前端优化提到的渲染时间来粗略估计,从资源下载到渲染加起来都不到 10ms,如果不是浏览器的一些限制,绘制帧率应该能够远超 60 帧,进一步满足我们实现“即使刷新了也比没一些没刷新的实现还顺滑”。

上面的实现中,我将页面图标请求和页面文档进行了拆分,在书签数量和图标种类不多的场景下,或许并不是最优的方案,但是一旦书签数量级到几百、上千之后,你会发现图标拆分可以极大地提升性能。

当然,为了满足数量比较少的场景,我也对合并输出进行了实现,算上网站 favicon 获取,一共只有两个请求。在书签不是很多的时候,渲染性能甚至比文档和资源拆分输出效率更好。

Flare 请求合并模式下的网络请求

图标资源优化

Flame 使用的方案是读取后端接口配置,从前端脚本中动态创建 SVG 图标并插入文档中,Flare 程序默认的方式则是将 SVG 和文档拆分,以应对大量书签状况下的页面性能问题。

虽然解决了页面性能问题,但是服务端 IO 问题却会伴随而来,所以这里还需要处理资源在服务端的释放和读取问题,尽量将资源的磁盘 IO 变为零。

听起来比较玄乎,但其实结合代码生成的方式,还是蛮好实现的。当然,因为 Go 存在自动 GC,所以在不同的资源被使用的时候,会出现大量内存的分配,影响效率,这里可以考虑使用持久化方案来解决问题,处理起来挺有意思的,受限于篇幅和主题就不展开啦。有一部分我在前两篇文章中提到了,关于 Golang 嵌入资源的使用和优化。

前段时间折腾 Go Emed 的记录

比如,在不针对 HTTP 服务实现做任何优化、限制运行资源为两核心的前提下,仅优化资源 IO 后,能达到稳定 3ms 输出资源,每秒提供2万7千次以上的响应服务。

容器镜像的优化

除了常规优化之外,容器时代的应用,镜像优化也是非常关键的。容器优化方式,我在前面的文章反复提过多次,所以也不再展开了,感兴趣可以自行翻阅之前的内容。

# docker images | grep fla
soulteary/flare       0.1.1      22b18ad73c66        12MB
soulteary/flaume      2.2.0      b39fffc0ca81        152MB
pawelmalak/flame      2.2.0      fa47c93c0af6        179MB
pawelmalak/flame      2.0.0      729b0fcea7f0        190MB

可以看到,相比较原版程序,优化后的程序在本地解压后的尺寸大概是之前十五到十六分之一。

额外的优化

如果我们使用 lighthouse 针对 Flame 前端实现进行测试,能够看到前端程序在实现上的一些小问题,得分虽然四个环绿三个,但是只有一个环是绿色的。

Flame 应用 Lighthouse 得分

在重新实现的过程中,除了简化结构,调试实现之外,还顺手将这四个圈都打到了满分(Chrome 版本 v97+)。

Flare 应用 Lighthouse 得分

最后

聊到这里,相信你已经了解了我是怎么做的啦,如果你对 Flare 感兴趣,并且也需要一个简单的导航程序,可以访问项目 https://github.com/soulteary/docker-flare,来亲自上手试试。

到此这篇关于Flare应用前后端性能优化的文章就介绍到这了,更多相关Flare性能优化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 聊聊Flare应用前后端性能优化问题

    目录 写在前面 应用性能问题分析 对于我不适用的功能 前端架构中的问题 后端架构中的问题 针对应用进行改进 调整前端实现 调整后端实现 图标资源优化 容器镜像的优化 额外的优化 最后 两周前,在给颜值在线的 flame 提交了几个 PR 之后,我将它封装成了容器,用于书签和在线应用的管理. 但是在迁移个人书签的过程中,我发觉 flame 在性能上的表现并不是特别好,于是我做了一个改良版:flare. 写在前面 在聊 flare 之前,我想先聊聊 flame. flame 是一个颜值在线的导航页项

  • webpack学习教程之前端性能优化总结

    前言 曾几何时,我们是如上图的方式引入JS资源的,相信现在很少遇见了.近年来Web前端开发领域朝着规范开发的方向演进.体现在以下两点: 1.MVC研发构架.多多益处(逻辑清晰,程序注重数据与表现分离,可读性强,利于规避和排查问题...) 2.构建工具层出不穷.多多益处(提升团队协作,以及工程运维,避免人工处理琐碎而重复的工作) 模块化开发 将前端性能优化理论落地,代码压缩,合并,缓存控制,提取公共代码等 其他的还包括比如你可以用ES 6 或CoffeeScript写源码,然后构建出浏览器支持的E

  • 浅谈react前后端同构渲染

    前后端同构渲染:当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互.然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这样,耗时相对较长.前后端同构渲染可以在页面初次加载时把所有地方渲染好一次性响应给客户端 实现方式:保证包管理工具和模块依赖方式一致 包管理工具-npm管理,保证前后端都使用同一个兼容包 模块依赖方式-webpack,保证前后端都采用commonjs的依赖方式,确保代码可以互相依赖 服务端如

  • 基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架

    前言 使用Node做前后端分离的开发模式带来了一些性能及开发流程上的优势, 但同时也面临不少挑战.在淘宝复杂的业务及技术架构下,后端必须依赖Java搭建基础架构,同时提供相关业务接口供前端使用.Node在整个环境中最重要的工作之一就是代理这些业务接口,以方便前端(Node端和浏览器端)整合数据做页面渲染.如何做好代理工作,使得前后端开发分离之后,仍然可以在流程上无缝衔接,是我们需要考虑的问题.本文将就该问题做相关探讨,并提出解决方案. 由于后端提供的接口方式可能多种多样,同时开发人员在编写Nod

  • 基于NodeJS的前后端分离的思考与实践(五)多终端适配

    前言 近年来各站点基于 Web 的多终端适配进行得如火如荼,行业间也发展出依赖各种技术的解决方案.有如基于浏览器原生 CSS3 Media Query 的响应式设计.基于云端智能重排的「云适配」方案等.本文则主要探讨在前后端分离基础下的多终端适配方案. 关于前后端分离 关于前后端分离的方案,在<基于NodeJS的前后端分离的思考与实践(一)>中有非常清晰的解释.我们在服务端接口和浏览器之间引入 NodeJS 作为渲染层,因为 NodeJS 层彻底与数据抽离,同时无需关心大量的业务逻辑,所以十分

  • 基于NodeJS的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践

    淘宝网线上应用的传统软件栈结构为 Nginx + Velocity + Java,即: 在这个体系中,Nginx 将请求转发给 Java 应用,后者处理完事务,再将数据用 Velocity 模板渲染成最终的页面. 引入 Node.js 之后,我们势必要面临以下几个问题: 技术栈的拓扑结构该如何设计,部署方式该如何选择,才算是科学合理?项目完成后,该如何切分流量,对运维来说才算是方便快捷?遇到线上的问题,如何最快地解除险情,避免更大的损失?如何确保应用的健康情况,在负载均衡调度的层面加以管理?承系

  • 基于NodeJS的前后端分离的思考与实践(一)全栈式开发

    前言 为了解决传统Web开发模式带来的各种问题,我们进行了许多尝试,但由于前/后端的物理鸿沟,尝试的方案都大同小异.痛定思痛,今天我们重新思考了"前后端"的定义,引入前端同学都熟悉的NodeJS,试图探索一条全新的前后端分离模式. 随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻

  • 大家须知简单的php性能优化注意点

    什么情况,可能遇到性能问题: 1.php语法使用的不恰当 2.使用php语言做了它不擅长做的事 3.用php语言连接的服务不给力 4.php自身的短板 5.我也不知道的问题 一般情况:php性能问题不超过二分之一(一般30%~40%) php性能问题解决方向: php语言级的性能优化->php周边问题的性能优化(连接的服务,网络环境)->php语言自身分析和优化 (php语言级) 优化点:少写代码,多用php自身能力 问题:自写代码冗余较多,可读性不佳,导致性能低 为什么低:php代码需要编译

  • jQuery性能优化技巧分析

    本文较为详细分析了jQuery性能优化技巧.分享给大家供大家参考.具体分析如下: 一.使用最新版本的jQuery类库 jQuery新版本会较上个版本进行Bug修复和一些优化,不过需要注意的是,在更换版本之后,不要忘记测试你的代码,毕竟有时候不是完全向后兼容的. 二.使用合适的选择器 jQuery选择器性能最佳到最差方式如下: id选择器,如$('#id', context) 标签选择器,如$('p', context) class选择器,如$('.class', context) 属性选择器,如

  • mpvue性能优化实战技巧(小结)

    最近一直在折腾mpvue写的微信小程序的性能优化,分享下实战的过程. 先上个优化前后的图: 可以看到打包后的代码量从813KB减少到387KB,Audits体验评分从B到A,效果还是比较明显的.其实这个指标说明不了什么,而且轻易就可以做到,更重要的是优化小程序运行过程中的卡顿感,请耐心往下看. 常规优化 常规的Web端优化方法在小程序中也是适用的,而且不可忽视. 一.压缩图片 这一步最简单,但是容易被忽视.在tiny上在线压缩,然后下载替换即可. 我这项目的压缩率高达72%,可以说打包后的代码从

随机推荐