5000字总结面试常问前端项目优化究竟怎么来做?
我相信绝大部分的同学面试的时候都碰到过面试官问的一个问题,那就是前端项目优化
。这是一个非常发散的问题,涉及到的面很广。因此,这就很考验一个人的组织能力,尽可能的让聆听者能够知道你做了什么
,哪些方面
,怎么做的?
等等。
我待过的公司都算是电商相关的团队,业务多数以
中台
、后台
、商城
为主,在此期间做过的一些事情如果不做一些梳理真的很难在短暂的头脑风暴中展现出来。
如何下手?
首先,还是老样子,先给一张导图,后面慢慢来完善。如下图所示:在做前端优化的时候可以由面到点的进行切入。
那么整体的工作逻辑大体上是这样的:
基本的工作逻辑是为了避免我们发生一些问题的关键就是关键性的问题
这种无厘头的状况发生,能够给工作的内容有迹可循,做到有产出,有结果,拿数据说话。
接下来,就会从不同的维度来去谈谈,前端的优化该怎么来做。
分析
从分析出手,任何需要做的事情在没有明确具体需求的过程中,都是很难去行动的。举个例子:
今天,领导安排了一个很重要的客户要我去接待,说让我用尽一切办法一定要把客户招待好了。
接到任务的时候,大多数人都是一头雾水,或许有经验的话以前做过相关的工作,能有一些通用手段可以勉强应付,但是人与人之间是不同的,所以要让客户满意,必须得下一番功夫对客户做一些调研,方便下一步投其所好的安排接待工作。
故事结束。🛎️
回到文章本身,前端优化不也是一样的道理吗,接到任务的时候,大部分的前端同学都知道一些通用的优化手段,但是想要再进一步还是需要和接待客户一样,先分析当前的项目,发现问题,针对问题的本身排列优先级后才能去实施具体的手段。
下面,来看下几种常见的分析手段:
构建工具分析
传统项目中,大多数同学都使用的是webpack
做为前端项目的构建工具。针对构建工具的优化,无非就是两个方面:
- 体积:包体积是否存在杂质,能不能过滤出去?
- 构建速度:构建速度影响开发调试效率和发布相关环境的节奏。
针对体积的分析手段,可以使用webpack-bundle-analyzer
来分析当前项目中是否存在重复依赖
, 影子依赖
, 模块体积差异
等等问题,使用交互式可缩放树图可视化
的来梳理自己项目应用中的问题,然后去找到并且优化它。
针对速度分析, 可以使用speed-measure-webpack-plugin
来分析当前每个bundle模块
打包构建时的时间数据,分别会包含loader
, plugin
等处理的时间。根据相关的数据来定点模块,判断是否有优化的空间。
项目分析
项目分析就落实到了具体的点上,针对当前项目的具体现状来去做问题 task 的点对点优化。而分析项目的手段大体上有以下几个。
- NetWork: 网络面板,用于侦测浏览器资源下载与上传的能耗视图。
- Performance: 性能面板:用于侦测浏览器运行时的性能表现,得出项目运行的时序图,同时也可以分析页面的一些隐式问题,如
(内存泄漏)
。 - Lighthouse: 性能评测(灯塔),基于当前浏览器页面的阶段性加载现状来为用户提供结果分析数据指标报告。
- 探针: 前端框架如
React
有相关Profiler
的探针API
和chrome插件
,能够为开发者探测代码编写时的问题,避免重复或者是频繁异常的Repeat rendering
。
从项目着手,通过以上的
分析手段
可以迅速的来分析项目中存在的大部分问题。
标准化?
分析是手段,通过人工来执行很明显是不靠谱的,且对个人消耗是非常疲劳的?
那么,能不能标准化且自动化?
答案肯定是:
答案:
能行
,但并不是都行
。
相对应具备相应规模的前端团队都会有自己的性能平台
,为线上的前端进行侦测,从接口调用
、首屏性能
、流量保活
等等一系列的性能指标做度量平台。
在开发过程中,通过设置CI流水线
做工作项,生成对应的Lighthouse
报告,根据报告做到侦测,做一些分析保质的工作,根据团队的侧重优先级来定义是否作为卡点?
对部分能够实施标准化的手段可以做一些自动化的事情,能够尽可能的来降低前端同学的日常烦恼。😣
甚至目前市面上也有一些性能评测平台可以为你解决一些困扰,比如大家耳熟能详的WebPageTest Pro
。如下图所示:
果然,多数的问题都可以用充钱来解决。~
如何做优化?
在上一个章节中,我详细的讲了分析的手段。这一章节则是针对分析出的问题来做的一些实施手段了。根据之前的流程所示,当前我们应该处于实施阶段,如下图所示:
既然谈到了实施,那么内容就相对的可以多数一点了,大多数的情况下,从构建工具到项目都能够有一些可操作的实施空间的。
构建工具优化
在之前的章节构建工具分析章节中,我通过webpack-bundle-analyzer
和speed-measure-webpack-plugin
两个分析插件来进行构建产物的分析。那么分析出结果后?又该怎么去做一些优化呢?
包体积大小
首先,针对包体积大小做一些调整,能做的事情大体上有以下几点:
- 按需加载:针对项目中比较大型的工具类库,如
lodash
、UI Design
、use-hooks
等类库,如果只是使用很小的一部分,就需要避免全量加载的情况。 - tree-shaking:通过 ESM 的特性,可以在构建阶段将项目模块中的死代码进行移除,从而保证干净的结构和最小的文件大小。
- minify:通过
webpack
相关的插件,可以为代码产物做混淆压缩,如JS minify
与CSS minify
,当项目的代码越大的时候,压缩的效果就越明显。 - CDN 依赖:对于部分共有依赖可以将其放置在 CDN 中来进行加载,能够极大幅度的减少本地项目产物的体积大小,缺点就是无法进行按需加载,占用浏览器请求资源,具体的收益可以自己权衡。
构建速度
其次就是构建速度了,可以从以下几个方面来着手:
- 持久化缓存:
webpack5
本身集成,通过cache
字段开启即可,webpack4
可以通过cache-loader
来实现,不过不足的地方是cache-loader
仅对loader
起作用。(目前该loader看起来已经暂停维护了
)。 - 多进程构建:通过
thread-loader
可以开启多进程构建,可以提高部分环节的性能效率,比如babel-ast
解析等等。webpack4
的话则是使用happypack
来实现。 - Building:使用基于
Rust
编写的高性能编译工具来替换默认的babel
,比如将babel-loader
替换为swc-loader
,这个是一个可选的操作,目前市面上多数cli
都支持了该选项,以供开发者抉择。 - Dll: dll 可以将一些依赖库提前进行打包,能够有效的降低打包类库的次数,只有其修改后才会进行重新的构建,通常用于公用类库的处理。可以使用
DllPlugin
来配置对应的效果。 - Bundless:
Vite
是市面上非常火热的Bundless
方案之一,已经可以看出越来越多的人开始使用它来代替传统Bundle
方案,其优秀的构建速度
,HMR
深受好评。
现如今,目前市面上的集成框架
已经做了 80%的事情,很多通用型的优化并不需要太繁琐的配置了。比如Umi
,Nextjs
等等都做到了开箱即用的开发体验。
以上的方案仅仅是代表一些选择。在日新月异的生态更新来,会有越来越多的平替方案出现,部分方案也会因为缺少维护慢慢停滞。
项目方案
聊完了构建工具的一些基础优化后,接下来的话就是主要围绕项目上的一些方案,其中主要是从资源加载
、网络相关
,交互设计
等几个方面做切入点吧。
资源加载
下面是netWork
板块的相关时序图,通过它可以来分析得出项目当中资源加载的详细情况。而在这其中,能来做哪些事情呢?
- 懒加载:懒加载我对齐的定义是延迟资源的加载,直到齐第一次触达时在进行资源的引入和展示,这项技术被广泛用于
spa-router
、图片资源
等非阻塞性资源的优化上。对于缩短关键路径渲染非常有帮助,如各位同学耳熟能详的首屏优化
。 - 预加载:预加载的目的就是通过在渲染前预先拿到需要展示的数据,从而缩短关键承流页面呈现给用户的渲染时间,大多数情况下可以分为
首页预载
和关键页预载
两方面。- 首页预载:
Web
端提供了相关的pre-load
、pre-render
、pre-fetch
相关 API 实现。小程序方面也提供了相关冷启动
获取数据的能力,例如getBackgroundFetch
。对于追求极致的性能体验的话不妨一试。 - 关键页预载:关键页预载的话就根据自身业务来实现了,参考商城业务中
商品详情页
、营销会场
等页面都是用户比较熟悉的页面。因此,多数时候都会将页面跳转
与数据请求
共同进行的。
- 首页预载:
- 接口优化:耗时较久的接口如果阻塞核心链路,可以将其拆成多个粒度小的接口来渐进式的调用以此来保证页面优先加载。反之亦然,如果接口零散占用了太多的请求资源,则可以将其聚合成一个完整的大接口来获取数据。
- 缓存:浏览器缓存策略大体上分为
强缓存
和协商缓存
。利用好缓存策略可以有效的降低资源的重复加载,从而提高网页的整体加载速度。
网络相关
网络这块优化相对而言,我和很多同学一样,都非常薄弱。所以再问到的时候多少有些懵逼。
虽然自己可能做过,但一下子想说出来,还是有点难的。
从输入 URL 到页面展示到底发生了什么?这是很多同学面试都碰到过的一个面试题。不懂的同学可以点击这里进行学习。
在这个过程中,会有两个阶段大家耳熟能详,分别是DNS解析
和HTTP连接(三次握手)
。它们是这个过程中相对耗时的两个部分。基于此,可以针对网络来对项目做以下一些优化:
- DNS 预解析: 了解
DNS解析过程
的同学都知道,相当耗时。而浏览器也提供了dns-prefetch
的相关操作,用于抢先对origin
进行解析。 - PreConnect:既然 DNS 可以预解析,那么请求连接的话也相对应的可以抢先执行。
- 内容分发网络 CDN:通过
内容分发网络CDN
可以使用户就近获取所需要的内容资源,从而降低网络拥塞
,提高用户访问响应速度
和保证访问资源的命中率
。 - HTTP/2:尽可能使用
HTTP/2
的新特性,它会带来一些意想不到的小惊喜。感兴趣的可以看《HTTP2 简介和基于 HTTP2 的 Web 优化》 - Gzip:很早之前会开启 Gzip 的压缩方案来减少网络实际传输数据的大小,服务器会将请求资源进行压缩后传输给客户端。
交互体验
好的交互体验能够让用户忽视一些产品本身的缺陷,属于正向信息传达,而不是让用户觉得是你的产品发生了问题。而在这过程中,也有着一些良好的手段可以进行实施。比如以下几种常见的方案:
- 加载态:提到加载态,可能大多数同学第一想到的就是
loading
,基于loading
的实现之上,很多产品都会对部分页面制作骨架组件/骨架屏
,它的优点就是表达能力强,相比于简单的加载loading
而言,骨架组件
能够给用户呈现预期的页面与区块的显示效果。 - 虚拟列表:虚拟列表已经是近年来老生常谈的一个优化了,其场景主要用于页面中需要展示大量数据时的一个措施手段,如
收获地区选择
、无限滚动场景
,任何需要堆叠数据显示的页面中或多或少都会出现它的身影。- 想了解详细的场景和实现过程的可以查阅我之前写的相关文章《百万 PV 商城实践系列 - 前端长列表渲染优化实战》
所有的交互都是为了让用户有更好的体验,
加载态
和虚拟列表
只是其中比较典型的代表罢了。
浏览器
- 回流和重绘: 回流和重绘是一个消费资源的事情,一旦浏览器发生频繁的回流,会造成浏览器资源的紧张,导致项目或多或少出现卡顿的问题。现如今浏览器虽然已经为我们做了
Layout
相关的合并优化。但还是要从根本上去减少不必要的情况发生。而解决的方案大多数如下几种:- 代码编写:样式集中改变,可以有效减少重排次数,尽可能不要频繁的去获取和写入 DOM,需要操作时应提前在内存中准备好对应的操作元素,从而一次性的将其进行读写。
- 隐藏修改:当涉及到 DOM 元素的频繁操作时,尽可能让其先排出文档流,直到最终操作完成后在将其渲染到页面上,以此来降低回流和重绘的频率。
- GPU 合成:对于动画,可以使用
transform
代替position
做一些事情,现如今大部分浏览器都会对transform
进行借用GPU
资源来做渲染优化。
- RAF&RDC:RAF&RDC 分别是
requestAnimationFrame
和requestIdleCallback
的简称,合理的使用可以避免频繁逻辑修改导致延迟关键事件,如动画和输入响应的交互。一个是针对动画帧效果,一个针对页面执行的逻辑。 - 事件委托:通过事件委托来绑定对应的操作,可以有效的减少相关内存的消耗。现如今,我们已经不在需要太过关注原生事件的处理了,多数情况下框架类库都已经能够很好的开始工作。
其他的内容我就不一一赘述了,上面的几个点是比较常使用到的优化点,多数会运用于项目。
图片
在 web 项目当中,图片是除了文字内容展示外最多的一种方式,那么除了之前提到过的懒加载外还有其他的优化方式吗?
答:很明显有,不然也不会开一个标题单独做一个章节了。
市面上比较多的一些方案:
- WebP:
webp
与png
一样,是一种图片格式,因为使用更优的图像数据压缩算法
、更小的图片体积
因此能够带来更好的访问速度优势。需要注意的是可能会朋友不支持 webp 格式的环境问题,这时候就需要进行降级处理避免出现用户无法访问图片的体验。 - 渐进式加载:渐进式是一个过程,渐进式加载的话就是让一张图片从模糊到清晰慢慢展示的一个过程,避免用户因为网络问题导致图片加载失败看到不友好的体验,本质上的话属于加载状态的一种。和骨架屏区别不大。
- 图片切割:在业务当中,会看到类似
商品详情图
、宣传海报
等相关的图片展示,它本身是一个非常长的图片,且体积因为要保真的原因无法做一些压缩。因此针对类似的场景就可以选择图片切割的方案,将长图切割成若干份小图进行展示,搭配之前提到过的一些优化方案可以完美的避免加载渲染问题。 - 图片 CDN:使用图片 CDN 的好处除了可以获得内容分发来提高响应速度外,还可以使用其提供的
图片裁切
功能动态的定义分辨率大小
、清晰度展示
等辅助其在页面上的展示。
在之前的《百万 PV 商城实践系列 - 前端图片资源优化实战》文章中,我非常详细的将图片的一些优化方案都落地到了业务项目当中,且收到了非常多的正向反馈。如果想细致的了解其中的细节,可以查阅看看哦。
写在最后
很多同学不明白为什么需要拿数据结果,优化完了上线不就行了吗?为什么还要产出文档。
从数据结果来说,不管是对内还是对外数据无疑是最有说服力的。它能直接的体现整个优化的一个结果。
从撰写文档来说,不管是对组内还是组外,文档都是给予他人参考上手最快的材料,很多最佳实践的记录不都是从文档开始的吗,当他人阅读了你的文档,那么就可以比较快速的抄作业,而不是从头开始再来一遍。
最主要的是,数据结果和文档是复盘不可或缺的一部分。
总结
到此,整篇前端优化的文章就到此结束了,我也总结了一份完整的思维导图,如下:
不知不觉已经写到了 5000 字左右了,由于算是一个内容的总结扫盲,很多细节肯定没有那么详细,想要了解其中具体的细节,我给的建议是都去实践一次,知其然才能知其所以然。
写这篇文章的目的是因为最近也在看机会面试,经常被问到。这几年做的事情零零散散的很难总结成一个体系来输出,所以特地花时间做了一些整理。
我相信也有不少同学也会碰到类似的问题,如果对你有一些帮助和启发,可以给我点点赞
。有一些好的建议和想法也可以在评论区
或者其他渠道
做互动交流
。