笔者是一个六年前端,没有大厂经历,也没有什么出彩的项目,所以今年以来,前端现在这种行情下并没有收到多少面试,但是为数不多的面试中,百分之九十都问到了性能优化的问题,而且问题都出奇的一致:
平时的工作中你有做过什么性能优化?
对于这个问题其实我的内心os是(各位轻喷~):
你们怎么都这么爱问性能优化的问题?我的简历中也没有写到这个啊。
你们的业务都这么复杂吗?怎么动不动就要性能优化?
你们的代码写的这么拉吗?不优化都不能使用吗?
性能优化是一个高级前端的必要技能吗?
首先客观现实是笔者平时工作中的业务并不复杂,需要性能优化的地方确实不多,一些存在性能瓶颈的大多是使用了其他团队开发的东西,比如播放直播视频的SDK、3D地图引擎等,也找过他们进行优化,但是没用,他们也优化不动。
所以每次被问到这个问题我就很尴尬,说工作中没有遇到过性能问题,估计面试官也不信,直接说没有做过性能优化,那又显得我这个六年经验的前端太水了,连这个都不做,所以每次我只能硬说。
没吃过猪肉,还没见过猪跑吗?其实性能优化的文章我也看过很多,各种名词我还是知道一点的,比如:
- 性能问题排查:
1.数据埋点上报
2.使用控制台的NetWork、Performance等工具
3.webpack-bundle-analyzer插件分析打包产物
- http相关:
1.gzip压缩
2.强缓存、协商缓存
- 图片相关:
1.图片压缩
2.图片懒加载
3.雪碧图、使用字体图标、svg
- webpack相关:
1.优化文件搜索
2.多进程打包
3.分包
4.代码压缩
5.使用CDN
- 框架相关:
1.vue性能优化、react性能优化
2.异步组件
3.tree shaking
4.服务端渲染
- 代码实现
1.按需加载,逻辑后移,优先保证首屏内容渲染
2.复杂计算使用web worker
3.接口缓存、计算结果缓存
4.预加载
5.骨架屏
6.虚拟滚动
等等。
但这些绝大部分我并没有实践过,所以我都说不出口,说我没有机会实践也行,说我没有好奇心不好学不爱思考不主动发现问题也行,总之结果就是没有经验。
所以通常我硬着头皮只能说出以下这些:
1.开发前会花点时间梳理业务,全局视角过一遍交互和视觉,思考组件划分,找出项目中相似的部分,提取为公共组件和通用逻辑。
2.代码开发中尽量保证写出的代码清晰、可维护,比如:清晰的目录和文件结构、添加必要的注释、提取公共函数公共组件、组件单向数据流、组件功能尽量单一等。
3.时刻关注可能会存在性能问题的部分,比如:
路由组件异步加载
动态加载一些初始不需要用到的资源
频繁切换的组件使用KeepAlive进行缓存
缓存复杂或常用的计算结果
对实时性不高的接口进行缓存
同一个接口多次请求时取消上一次没有完成的请求
页面中存在很多接口时进行优先级排序,优先请求页面重要信息的接口,并关注同一时刻请求的接口数量,如果过多进行分批请求
对于一些确实比较慢的接口使用loading或骨架屏
懒加载列表,懒加载图片,对移出可视区的图片和dom进行销毁
关注页面中使用到的图片大小,推动后端进行图片压缩
地图撒点时使用聚合减少地图引擎渲染压力
对于一些频繁的操作使用防抖或节流
使用三方库或组件库尽量采用按需加载,减少打包体积
组件卸载时取消事件的监听、取消组件中的定时器、销毁一些三方库的实例
我工作中的实践也就以上这些,其实就是写代码的基本要求,另外我觉得如果业务复杂,以上这些也并不能阻止性能问题的出现,更多的还是当出现了问题,去思考如何解决。
比如我开源的一个思维导图项目mind-map,当节点数量多了会非常卡,调试分析思考后发现原因是做法不合理,每次画布上有操作后都是清空画布上的所有元素,然后重新创建所有元素,数据驱动视图,原理非常简单,但是因为是通过svg
实现,所以就是DOM节点,这玩意我们都知道,当节点数量非常多以后,删除节点和创建节点都是非常耗时的,所以数据驱动视图的框架,比如Vue
会通过虚拟DOM的diff
算法对比来找出最小的更新部分,但是我没有做。。。所以。。。那么我就自然的做了一些优化,比如:
思维导图场景,大部分情况下操作的其实就是其中一个或部分节点,所以不需要重新删除创建所有元素,那么就可以通过节点复用的方式来优化,将真实节点缓存起来,渲染时通过数据唯一的id来检查是否存在可复用节点,如果没有,那么代表是新增节点,那么创建新节点即可;如果有,那么就判断节点数据是否发生改变,没有改变直接复用,如果发生了改变那么判断是否可以进行更新,如果更新成本高那么直接重新创建;另外也需要和上一次的缓存进行对比,找出本次渲染不需要的节点进行删除;当然,为了避免缓存节点数量无限膨胀,也通过LRU缓存算法来管理
对于不影响其他节点的操作只更新被操作的节点
通过
setTimeout
异步渲染节点,留一些中间时间来响应页面其他操作将触发渲染的任务放到队列中,在下一帧进行处理,合并掉一些中间状态
对于鼠标移动和滚动的场景,通过节流来优化
进行一些取舍,早期节点激活时可以修改节点的所有样式,导致激活操作需要重新计算节点大小,更新节点样式,在多选和全选操作下非常耗时,所以后期改为只允许修改不改变节点大小的样式属性
其他一些细节优化:对于数据没有改变的操作不触发赋值或函数调用,一些不起眼的操作可能也是需要耗费时间的;改变了不涉及节点大小的属性不触发节点大小重新计算等
经过以上这些修改后,性能确实有了很大程度的提升,不过有些项目可以通过不断的优化来提升性能,但是有些可能就是设计缺陷,比如我开源的另一个白板项目,更好的方式其实是重做它。
写到这里其实并没有解决本文标题提出的问题:
为什么面试官这么爱问性能优化?
因为我没有怎么做过面试官,甚至面试经验其实都不太多,写这篇文章目的主要有两个:
1.想听听有面试官经验的各位的想法或建议
2.想看看和我有类似情况的面试者面对这个问题,或者说类似的问题是如何回答的
最后再牢骚几句:
有时会感慨时间过的真快,一转眼,作为一个前端已经工作了六年,即将三十而立却立不起来,这么多年的工作,更多的只是收获了六年的经历,但是并没有六年的能力,回过头看,当初的有些选择确实是错误的,也许这就是人生把。
作为一个普通的前端,在如今的行情下面试确实很艰难,尤其是我这种不擅长面试的人,不过话说回来,改变哪有不痛苦的,除了面对也没有其他办法。