一次对复杂的古董问卷系统项目的前端优化之旅

背景

年前接盘了一个项目的优化任务,这是一套类似“问卷网 / 问卷星”之类的,集问卷拖拉创建、预览、发布、数据收集、数据可视化报表等非常齐全的复杂的古董问卷系统,由于产物是出生于 jQuery 一招先,jQuery 称王称霸的年代。实现的逻辑当然是 Dom 操作为王的思路。加之、先前的开发作者是偏后端的全栈开发,因此其只专注于功能的实现,并没有考虑过前端性能的问题。前端代码的结构也是非常潇洒飘逸。因此,功能虽然基本能满足日常普通的问卷收集需要,但直到最近,忽然需要搞一次复杂的问卷收集,问卷题目量非常大,每题如单选、多选题的选项也差不多有 10+ 题,问卷中题目与题目之间添加的逻辑(行业内通用问卷逻辑:控制跳转、控制显示、组合逻辑、互斥逻辑、引入逻辑)有 200 多个。系统一下子爆露出严重的性能问题。首先,答题页面页面打开非常慢,答题页面每选择一题,都会卡死 20+ 秒无响应。

项目优化步骤之一 – 熟悉业务

熟悉业务!熟悉业务!熟悉业务!重点的事情要说三遍。二话不说,肯定是要先找遍与这项目相关的所有人去沟通、记笔记、尽可能去熟悉业务的一切。本来年前计划要出差飞北京去找相关人员熟悉系统。后面赶上疫情,只能远程进行。抓住相关的一切人员,挨个找他们讲解他们对系统所了解的一切。然后,把有用的信息全部记录下来。接着,就把自己当成小白用户去一步步把整套系统玩熟。

项目优化步骤之二 – 调试查找慢的原因

这时,要出动神器 – Chrome F12 调试。看看 Network,先看 waterfall 首先就发现大问题 html 模板居然要加载 10s+ 的时间,然后是 js,css 也有 3s+ 的情况。这是神马情况,哦是 jQuery UI 相关的资源没压缩。按 Size 和 Time 排序一下,超过 300K+ 的资源貌似不少……。
接着是代码层面,先定位到相关的 js 代码,直接在 Chrome F12 打开 sources 找到相关的资源 js 然后直接在上面添加计时神器,开始处添加 console.time("myTimer1") 结束处添加 console.timeEnd("myTimer1"),其中还可以添加变量去统计模块跑的次数,然后 Ctr + s 直接保存修改;再配合添加相关的断点跑到断点处,可以直接拷贝相关的变量 / 函数,直接拿去 Chrome 的 Console 面板命令行上去执行。很好,所有牛鬼蛇神无所遁形。所有慢的逻辑模块,全部分析出来了。运行多少时间,运行多少次全部被统计出来。

项目优化步骤之三 – 前端 + 服务器层面优化资源加载慢的问题

这是老掉牙的优化方法了,估计大家都会。前端方面,首先,代码结构调整,css 资源先加载,位置放 head,js 资源放 </body> 前;把该合并压缩的 js,css,进行合并压缩;最后使用服务器的压缩神器 gzip。gzip 开了之后,所有资源加载时一下子全部被压到了 100K 以下……。一下子页面加载飞了起来。基本是秒开了。问题来了,部分旧手机、旧手机自带的浏览器不支持 gzip?WTF!!!没关系,对症下药,让后端在 Nginx 服务器上装上 incubator-pagespeed-ngx 插件,双重压缩合并资源;同时为了减轻服务器静态资源加载的压力,花个一百几十块买了个  CDN,支持压缩图片资源的 cdn,不得了了,顺带解决了产品经理一值头痛的图片压缩问题,页面飞了起来。但先别开心得太早,页面题目多时,jquery.mobile 会有渲染过慢的问题,太坑了,苦思冥想后,得出的解决方案是去掉 jquery.mobile,但保留其样式;自己按 jquery.mobile 渲染规则,服务器端直接输出 Dom 节点来处理(服务器端渲染 SSR 的思想),这样就免去了其渲染时间,当然还要自己用 jquery 处理来补充部分 jquery.mobile 组件的交互效果;这里面有些技巧,比如 radio / checkbox 可以把 labelinput 互换,然后使用 .my-class:checked + label 的方式来设置选中样式;然后,使用 :first-of-type / :last-of-type 的写法来代替 jquery.mobile 自带的 ui-first-child ui-last-child反正,css 能解决的问题就不用写 js 控制。这样,最后的坑也顺利被填了。

项目优化步骤之四 – 最难的部分:js 代码逻辑优化(要写算法、要写算法、要写算法!不碰算法的前端不是一个好前端!!!)

上面把所有的慢的模块,跑完 js 耗费的时间,跑的次数都知道了,现在对业务逻辑的熟悉程度对本优化环节的就起关键作用了。别急着乱动业务关键逻辑,先单独拉个用于优化代码分支。好,现在在新的分支上面,可以为所欲为了。先把最耗费时间的代码模块 / 被循环 / 被递归跑的次数最多的模块都列出来。有没有一些业务逻辑代码是没用的 / 或者已经不在使用了?有没有一些被循环 / 被递归跑业务模块代码可以减少循环 / 递归次数?有没有一些功能可以在用户体验层面修改 / 去掉后,用户也可以接受或者体验更好?有没有可以开源截流的可优化的逻辑?jQuery 变量 / js 变量 / 方法可缓存起来使用?最后,也是要跟后端商量的部分,有没有一些超级耗时的业务逻辑,如果不放前端处理,而放在后端处理其实是很快的?这一系列的灵魂拷问结合反复不断的修改 / 调试。最后得出了一个折中的,用户体验非常好的结果。顺利收官……

关键处理思路 – 把 Dom 处理与数据处理解藕分开,数据采用准多叉树的数据结构

一、页面初始化时,先把所有的逻辑及 题目 Dom 缓存为 (key, value) map 对,然后同时同步创建对应的数组

二、以题目 id 为准,构建准多叉树(注意:有些节点会重合,注意要去重,去重思路很简单使用 (key, value) map 对,map 一下自动去重);先构建第一层树,然后把树节点自上而下去重合并完成整棵树。

三、执行答题逻辑时,当要按顺序执行当前节点的儿子、孙子的逻辑前,先判断有没有必要执行(如果题目没回答,即不需要执行当前逻辑)。

四、按需不断调整生成树的算法

写在最后

优化是优化,不到万不得已,千万不要重构。重构一时爽,构错火葬场……

    1. 本文作者:Nelson Kuang,欢迎大家留言及多多指教
    2. 版权声明:欢迎转载学习 => 请标注信息来源于http://www.a4z.cn/fe/2020/06/30/fe-optimation/

作者: 博主

Talk is cheap, show me the code!

发表评论

邮箱地址不会被公开。

Captcha Code