⚡大厂面经 - 性能监控和性能优化篇

2,461 阅读8分钟

前言

给你一个网站,你如何知道它的性能优化做的怎么样?

工作中做过哪些性能优化方面的尝试?

面试中,往往会面临上面的灵魂拷问~~~ 我之前一直不明白面试官老问我这个干嘛?就非得是面试造火箭,工作拧螺丝吗!

后来,一个大佬解答了我的疑惑,这问题是一个很开放性的,包括了浏览器渲染过程、CSS/JS阻塞、重绘、重排、SSR、打包体积优化等很多知识点,可以层层递进,快速分别出面试者掌握的知识体系。

本文的重点会从性能监控性能优化方面入手。

image.png

正文开始

1,Navigation Timing API

是浏览器提供的API,主要用于获取与网页加载过程各个阶段的时间和性能指标。

比如想知道当前页面加载耗时,用法如下

image.png

下图是网页加载过程中的三个主要阶段,和对应的API分布。小伙伴们大概知道怎么回事就行,不需要刻意记住API名字,太长了,足以应付面试了。

image.png

接下来,对各个API进行一个解释,最后再来两个常见案例。

1.1,navigationStart

  • 前一个文档卸载时的时间戳 (如果无上一层页面时,从fetchStart开始)

1.2,unloadEventStart / end

unloadEventStart和unloadEventEnd的简写,下面的API同理

  • 前一个页面的unload时间戳
    • 无前置页面时? => 值为0
    • 前置页面域不同 => 值为0

navigationStart 和 unloadEventStart 的区别

这两个有点像,容易搞混。

  • navigationStart 取值先于 unloadEventStart

  • navigationStart 不限域,就是说,跨域的跳转也存在值,而 unloadEventLoad 必须是同域下的跳转才有值

1.3,redirectStart / end

  • 第一个http重定向发生的时间 / 最后一个http重定向完成的时间
  • 有跳转且是同域名不同页面 => 否则值为0
    • 单页面应用中,比如vue里通过router.push跳转的值为0

1.4,fetchStart

  • 浏览器准备好使用HTTP请求抓取文档的事件

1.5,domainLookupStart / end

  • 开始/重新建立连接的时间(只是建立
  • 如果是长链接 => 值等于fetchStart
    • 因为长连接不需要缓存,检查是否过期之类的

1.6,connectStart / end

  • TCP建立握手的开始 、 完成
  • 如果是长链接 => 值等于fetchStart
    • 因为长连接不需要缓存,检查是否过期之类的

1.7,secureConnectionStart

  • HTTPS连接建立开始时间

1.8,requestStart / end

  • 请求发起的真实时间
  • 读取本地缓存(强缓存/协商缓存)也会发起请求,也有值

1.9,responseStart / end

  • 请求返回数据的真实时间
  • 读取本地缓存(强缓存/协商缓存)也会发起请求,也有值

1.10,domLoading

  • 开始解析渲染DOM树 => 抛出readystatechange事件

1.11,domInteractive

  • 完成了DOM树的解析 => 抛出readystatechange事件
    • 这个时候并没有开始加载网页资源,比如echart,css,js

1.12,domContentLoadedEventStart / end

  • DOM解析完成后,开始 / 结束加载网页内资源的时间

1.13,domComplete

  • 整体DOM树解析完成 => 抛出readystatechange事件

1.14,loadEventStart / end

  • load事件发送给文档 / 回调执行完成的时间
    • 网络资源加载完成,dom渲染完成,可以获取canvas 的dom去渲染echarts

常见问答

网络请求的时间段

  • domainLookupStart - responseEnd 的阶段
    • responseEnd 减去 domainLookupStart得到的时间戳就是网络请求所花费的时间,下同理

静态渲染花费的时间段

  • domLoading - domComplete 的阶段

动态渲染,所有文件加载完渲染花费的时间段

  • domLoading - loadEventEnd 的阶段

一个网站的性能如何,通过上面的API进行相减就能计算出来了。


学到这里,再加上你自己脑子里零碎的其他性能优化片段,其实针对很多中小厂面试就可以了。

下面会从Google 提出的 Core Web Vitals性能指标继续分析网址的性能,并给出大概解决方案~~

知识点有点零碎,不过这已经是提炼过之后的了,还有精力的小伙伴可以继续看下去~~~

image.png

2,Core Web Vitals 网页核心的性能指标

Google 提出的网页核心的性能指标,主要从加载、交互、视觉稳定性对一个网页进行性能打分。

2.1,LCP - Largest Contentful Paint 衡量渲染性能

  • 衡量加载性能 - 前2.5s内进行最大内容的渲染
最大内容是神马东西?**
  • <img>元素
  • <svg>元素
  • <video>元素
  • 通过url()函数加载背景图片的元素
  • 包含文本整体节点的块级元素
什么导致了LCP值低呢?
  • 服务器响应慢
  • 阻断渲染的JS和CSS
  • 资源加载慢
  • 客户端渲染机器性能影响
🎉 提高LCP值的方法
  • 使用Service Worker缓存HTML离线页面,强缓存协商缓存缓存页面资源,减少浏览器对于资源的请求

  • 尽量减少资源组端渲染,减少重绘重排:CSS和JS做级联、内联、合并

  • 对图片进行优化 JPG WEBP => 降低资源大小 => 加快请求速度

  • 使用CDN加快请求速度 云资源管理

  • 利用工程化做项目优化 => HTML进行重写优化、ugliyfy压缩空格、去除注释、去除打印、调整格式

  • 提升首屏优化 => 懒加载、Tree Shaking资源按需加载、service worker、服务端渲染(SSR)

2.2,FID - First Input Delay

  • 衡量交互性能 - 页面首次输入的延迟应该小于100ms
🎉 提高FID值的方法
减少JS执行时间
  • 缩小并压缩JS文件
  • 延迟执行不需要的JS
  • 尽量减少用不到的polyfill
分解耗时的任务
  • 任何阻塞主线程超过50ms => 长任务
  • 长任务拆分成较小的异步任务
    • 把同步的长任务,分解成若干个异步任务
  • 优化算法降低复杂度,提升性能
  • 优化逻辑数据结构处理
worker:和长任务分解有点像,让主流程快速通行,减少输入的延迟
  • web worker / service worker

  • 相同点

    • 不能操作DOM
    • 运行在独立的线程中,不阻塞主线程
  • 不同点

    • web worker通过主子线程可以多线程处理,适用于执行复杂任务。 一部分活自己干,一部分分给工人干
    • service worker没有主子线程的概念。适用于处理网络请求、缓存数据和离线功能
      • 业务场景:service worker也能实现在线聊天,不过它不是实时的,而是通过缓存去取数据推给客户端
    // 1. web worker
    // main.js
    const myWorker = new Worker('worker.js');

    // 与mainthread之间通信
    myWorker.postMessage('hello');
    myWorker.onmessage = function(e) {
        console.log(e.data);
    }

    // worker.js
    self.onmessage = function(e) {
        console.log(e.data);

        self.postMessage(workerResult);
    }

    // 2. service worker
    // main.js
    navigator.serviceWorker.register('./service-worker.js');

    // serveice-worker.js
    self.addEventListener('install', event => {})
    self.addEventListener('activate', event => {})
    self.addEventListener('fetch', event => {
        event.respindwith(
            caches.match(event.request)
        )
    })

2.3,CLS - Cumulative Layout Shift

  • 衡量视觉稳定性 - CLS页面应当保持在小于0.1

  • 布局的移动可能发生在可见元素从一帧到下一帧改变的位置

    • 左右布局的一个页面原本比例是5:5,突然变成9:1,就会对值有影响
🎉 提高CLS值的方法
不使用无尺寸元素
  • srcset & sizes
    • srcset设置不同图片
    • sizes根据不同屏幕适配不同大小
    <img srcset"image.jpg300w,image.jpg100w" sizes"(max-width:600px)600w,(max-width:1000px)1000w">
减少内容的插入
  • 影响整体的布局
动态字体控制
  • 积少成多,字体的变化有时候也很影响CLS的值

    • 比如网页默认使用Arial,但是某个页面要用微软雅黑,如果浏览器已经预加载了微软雅黑,那么就可以快速渲染页面,如果没有预加载,那么浏览器就会先使用Arial,过了一会变成微软雅黑
  • <link rel="preload"> - 预加载字体

2.4,CWV 工具

  • Chrome开发者工具中的Lighthouse面板

image.png

image.png

上图可以看到LCP微微有点高,CLS表现比较好

  • Core Web Vitals Annotations

    • Google 提出的计算Core Web Vitals网页核心的性能指标的工具,是Chrome商店的一个浏览器插件

image.png

3,性能优化的另一种可能 - bigpipe

页面分解成若干的pagelet(片段)

  • 服务前端接收来自客户端HTTP请求
  • 存储层(有点Service Worker的影子)去获取数据 => 组装HTML,渲染基本完成的页面 => 响应给客户端
  • 有点类似Vite + SSR
    • 类似Vite的ESM分片渲染,然后服务端组装成HTML,给到客户端

总结

本文主要介绍了面试中关于性能监控和优化涉及到的知识点。

从浏览器API Navigation Timing API计算页面不同阶段花费时间 到 Core Web Vitals网页核心的性能指标的三大项(LCP、FID、CLS)和优化方案。

知识点可以说非常零碎而且多了。

汇总一下提升性能的方案就是

开发部分 => 减少重绘重排

打包部分 => 减少文件大小

部署和展示部分 => 提升加载和反馈的效率

其实,关于减少重绘重排,还有一部分浏览器渲染环境的知识点,有时间再小伙伴们罗列吧。

完结

这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。

欢迎转载,但请注明来源。

最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。

image.png