33.性能优化问题,老司机如何解决(上)

一直以来,性能优化是前端的重要课题,不仅实实在在影响产品性能,在面试环节也会被反复提及。无论应聘者是初入前端的新手,还是工作经验丰富的老司机,面试官都能在性能方面找到合适的切入点,对候选人进行考察。作为程序员,应该如何在平时学习、工作中积累性能优化方面的经验,保障产品顺畅体验?作为面试者,如何在面试流程中出色地回答性能相关问题?

前端性能是一个太过宽泛的话题,脱离场景和需求谈性能往往毫无意义。我相信很少有面试官会直接把:「前端如何优化性能?」——这样一个空架子问题抛出的。也不会有技术经理直接丢给你「把产品性能提升一些」这样的项目。毕竟这样的问题过大,根本让人无处下手。我们还需要针对具体场景和瓶颈来分析。

但是,如果真的有面试官这么问了呢?

如果是我,我也许会这样回答:

前端性能涉及方方面面,优化角度切入点都有所不同。我认为,主要可以分为:页面工程优化和代码细节优化两大方向。

页面工程优化从页面请求开始,涉及网络协议、资源配置、浏览器性能、缓存等;代码细节优化上相对零散,比如 JavaScript 对 DOM 操作,宿主环境的单线程相关内容等。

也正如上所答,本节课程也会基于以下两个大方向的相关知识进行梳理:

  • 页面工程优化
  • 代码细节优化

为了更好地还原真实场景,这两方面我都将配合两类面试题目来解析:

  • 开放例题实战
  • 代码例题实战

这个主题的知识点如下:


接下来,我们通过 2 节内容来学习这个主题。

开放例题实战

如上分析,面试官往往会根据面试者的实际经验或者性能的某一细分方向,进行深度提问,以了解候选人的知识储备以及在以往项目中的表现。

作为一个面试者,包括在蚂蚁金服、阿里淘宝某团队、头条、美团以及其他公司的多次面试流程中,我曾经被问到过:「在平时工作中做过哪些性能优化方面的项目?」

我是这样回答的:

因为我服务的是有亿级流量的 To C 型产品,因此平时工作中,在性能优化方面一直持续进行探索和迭代。除了代码细节方面外,较大型工程优化主要有 WebP 图片格式替换、资源打包和逆向代码拆分(按需加载)等。

WebP 图片优化

因为并不知道面试官需要考察的程度,以上回答可以避免自己「侃侃而谈」浪费时间的尴尬。在这样的面试场景中,我往往会把主动权交给面试官。大部分面试官会继续追问,比如他对 WebP 图片格式优化项目感兴趣,那我会从项目的立项、实施、收益的角度进行解答,表现作为一个项目负责人对优化项目的理解:

我们的产品页面中,往往存在大量的图片内容,因此图片的性能优化是瓶颈和重点。除了传统的图片懒加载手段以外,我调研并实施了 WebP 图片格式的替换。由于可能会有潜在兼容性的问题,因而具体做法是先进行兼容性嗅探。这一手段借鉴了社区一贯做法,利用 img 标签加载一张 base64 的 WebP 图片,并将结果存入 localStorage 中防止重复判断。如果该终端支持,则再对图片格式进行替换。这个兼容性嗅探过程,也封装成 promise 化的通用接口。

相关代码片段如下:

const supportWebp = () => new Promise(resolve => {
   const image = new Image()
   image.onerror = () => resolve(false)
   image.onload = () => resolve(image.width === 1)
   image.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA='
}).catch(() => false)

这时候,面试官往往会进一步关心项目收益情况。这就需要面试者根据实情作答,仍然以这个项目为例:

在具体上线时,我对 10% 的流量进行了分组切分。5% 为对照组,仍然采用传统格式;另外 5% 为实验组,进行 WebP 格式试验。 最终结果显示收益非常有限。 为此我进行分析:认为出现近似零收益的原因是图片服务的缓存问题。新转换的一批 WebP 格式图片由于没有缓存,因而在性能上打了折扣。为了验证猜想,我决定继续进行扩量试验并观察结果。果然,后续排除缓存问题之后,收益提升 25%~30% 左右。

这里,我们就涉及了工程优化中的一个重要环节:网络请求和缓存。这些内容我们会在后续课程网络环节《第 8-1 课:缓存谁都懂,一问都哑口》中具体展开。

通过以上回答,我如实讲述了出现的非预期 case,并说明遇见问题时,如何进行分析进而解决问题的一系列过程。这样的回答能明确表现出我确实做过该项目并进行了思考分析,最终落地。这类思路也更容易被面试官所接受。

更多关于 WebP 的好文章:

也由此看出,性能优化其实并不难做,重要的是解决问题的思路,以及解决过程中对于项目的把控和推荐。这些内容我们称之为「软素质」。

按需加载优化

如果面试官围绕着刚才列举的「资源打包和逆向拆分(按需加载)」方向提问,我仍然会采用同样的「思路」进行回答:

我接手项目之后,发现历史原有的资源打包配置并不合理,严重影响了性能表现。因此借助构建工具,对资源进行合并打包。但是,需要注意的是,我的策略并不是大刀阔斧地进行资源合并,因为这样会让 bundle.js 的 size 越来越大,所以也进行了逆向过程。

以实际页面为例:

如上,当点击左图播放按钮之后,页面出现视频列表浮层(右侧所示,仍为同一页面,类似单页应用)。视频列表浮层包含了滚动处理、视频播放等多项复杂逻辑,因此关于这个浮层的脚本我并没有进行打包合并,而是单独进行拆分。当用户点击浮层触发按钮后,再执行对这一部分脚本的请求。

工程化性能优化不仅需要做,还要用数据证明做法的合理性:

同时,我对用户点击触发按钮的概率进行统计,发现进入页面的用户只有 10% 左右会点击按钮,从而触发视频列表浮层。也就是说,大部分用户(90%)并不会看到这一浮层,延迟按需加载是有统计数据支持的。

通过这个案例,我们发现性能优化其实是一个开放式问题,非常依赖实践。读者可根据上面的例子,结合自己的项目进行回答。

虽然没有涉及代码实施,但是建立起项目意识和方向意识,这在工程化性能上非常难能可贵。上面提到的借助构建工具进行「按需加载」等内容,前面章节如《webpack 工程师 > 前端工程师》都会具体从代码角度给出示例。

当然上述举例的按需加载,我们并不是使用成熟的 webpack 工具链,而是采用公司内部封装的工程化工具。在几年前,这样的方案并不成熟,因此我写了一些按需加载的差距,配合自己的工程化工具使用。这一方面内容,很多面试官会很感兴趣,话题也可以延伸到 FIS 和 webpack 的比较,工程化工具的设计等话题。

讲不完的工程化优化

此外,工程优化方向还包括:

  • 图片懒加载
  • 雪碧图
  • 合理设置缓存策略
  • 使用 prefetch / preload 预加载等新特性
  • 以 tree shaking 手段为主的代码瘦身

这里不再一一举例,欢迎订阅此课程的同学结合自己的经历在评论区留言讨论或者直接向我提问,我会尽量拿出时间跟大家一起进行项目分析、描述。

以上是工程化性能的实际题目,总结一下:工程师需要对日常项目进行深入总结,结合产品角度、研发角度描述。在面试前就需要做到「心中有数,胸有成竹」。

另外,在具体的实现方向上,关于性能优化的切入点也有很多。比如:

  • 动画性能方向
  • 操作 DOM 方向
  • 浏览器加载、渲染性能方向
  • 性能测量、监控方向

这些方向并不是相互独立的,它们彼此依存,比如动画性能方向与浏览器渲染性能息息相关。这里我用一道经典的面试题来分析:「如果发现页面动画效果卡顿,你会从哪些角度解决问题?」

首先从动画实现入手:

  • 一般 CSS3 动画会比基于 JavaScript 实现的动画效率要高,因此优先使用 CSS3 实现效果(这一点并不绝对)
  • 在使用 CSS3 实现动画时,考虑开启 GPU 加速(这一点也并不总是正向效果)
  • 优先使用消耗最低的 transform 和 opacity 两个属性
  • 使用 will-change 属性
  • 独立合成层,减少绘制区域
  • 对于只能使用 JavaScript 实现动画效果的情况,考虑 requestAnimationFrame、requestIdleCallback API
  • 批量进行样式变换,减少布局抖动

事实上,上面每一点的背后都包含着很多知识点,例如:

  • 如何理解 requestAnimationFrame 和 60 fps
  • 如何实现 requestAnimationFrame polyfill
  • 哪些操作会触发浏览器 reflow(重排)或者 repaint(重绘)
  • 对于给出的代码,如何进行优化
  • 如何实现滚动时的节流、防抖函数

这些问题,我们会拿出其中几个在下一节分析。同时,订阅课程的朋友们,都可以在下节课末尾彩蛋区获得我在这些主题方向上收藏的文章。

总结

工程化优化是一个太大的话题了,本节课我们只是在「大面上」进行方向指引,以面试为角度抛砖引玉,更多具体代码细节实例,下节课我们会继续探讨。

另外,正因为这个话题的特殊性,一千个项目,就有一千个优化场景,也欢迎大家拿出来项目中的具体情况,跟我讨论。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 【性能优化指南】带你全面掌握前端性能优化 🚀https://segmentfault.com/a/11900000...
    达魔学院阅读 796评论 0 2
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,515评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,041评论 0 4