什么?Cesium.js 三维前端系统 性能提升 71.43%!首屏速度提升 200%!—— 前端性能优化,这么做就对了~:基于 LightHouse 的前端性能评价及代码优化实践流程
碳学长 2024-07-16 15:03:02 阅读 88
什么?Cesium.js 三维前端系统 性能提升 71.43%!首屏速度提升 200%!—— 前端性能优化,这么做就对了~:基于 LightHouse 的前端性能评价及代码优化实践流程
本文性能指标快速链接:
可以看到优化后,生产环境下 Performance 提升了 71.43%,性能分数提升显著。
生产环境下首屏页面完全渲染完成的时间缩短了 40% ,速度快了 66.67%
开发环境下首屏页面完全渲染完成的时间缩短了 62%~66% ,速度快了 166.67% ~ 199.99%
文件大小缩小了95.33%
耗时缩短了82.93%
网络请求数量下降了 46.75%,网络请求的总耗时大幅降低了75%。
可以看到优化后各类指标分数都有所提升,Performance 提升了 55.56%,Accessibility 提升了 1.11%,Best Parctices 提升了 13.46%,SEO 提升了 2.56%。
4%的提升
引言
该前端代码使用了 Cesium.js 等重型地图相关第三方库,采用了微内核架构,通过插件式开发,将各个插件(组件)组合为一个完整的系统页面,实现完整的系统功能。
当前该前端代码已经采取了一些优化措施,但依然还有可以继续优化的空间;其次当前前端开发模式下,还没有一套完整的前端性能优化方案、思路。
希望通过本次的实践,摸索各种前端性能优化的措施,同时实践各种优秀的代码开发规范,为后续各类前端性能优化探路,为规范化开发打下基础。
至于为何需要优化,一方面是除了当前还有很大的优化空间外,另一方面也需要让开发人员认识到前端性能的重要性。
而至于前端性能有多重要,可以通过下面一段话了解:
Google网站访问速度每慢400ms就导致用户搜索请求下降0.59%;
Amazon每增加100ms网站延迟将导致收入下降1%;
雅虎如果有400ms延迟会导致流量下降5%-9%。
一、LightHouse 性能报告 参数解读
1、Lighthouse 运行生命周期
Lighthouse 运行测评的过程有一套完整的生命周期,可以划分成三个主要流程:
**Collecting(收集数据):**首先是 Collecting 流程,这一步会调用内置的驱动程序(Driver) ,其作用是通过谷歌开发工具协议( Chrome DevTools Protocol) 调起浏览器,并创建新的 tab 请求待测评的站点,通过浏览器采集站点数据并将结果(称之为 Artifacts)保存在本地临时目录。
**Auditing(分析数据):**然后进入 Auditing 流程,读取 Artifacts 数据,根据内置的评判策略逐条进行检查并计算出各项的数字形式得分。
**Report(生成报告):**最后进行 Report 流程,将评分结果按照 PWA、性能、无障碍访问、最佳实践等纬度进行划分,以 JSON、HTML 等格式输出。
2、主要参数
使用 Lighthouse 对网站进行测评后,我们会得到一份评分报告,它包含了性能(Performance),访问无障碍(Accessibility),最佳实践(Best Practice),搜索引擎优化(SEO),PWA(Progressive Web App)五个部分:
可以查看性能、SEO各具体参数的评分,有自己的评分算法,通过颜色,可区分指标值所在段位。(因为网络环境的变化,每次测量也会有些许偏差,建议多次测量看平均情况)
指标比重
根据 Lighthouse 10 规则,Web 前端性能指标主要有 5 个:
「性能指标」 | 「权重」 |
---|---|
Speed Index (速度指数) | 10% |
First Contentful Paint (FCP-首次内容绘制) | 10% |
Cumulative Layout Shift (CLS-累积布局偏移) | 25% |
Largest Contentful Paint (LCP-最大内容绘制) | 25% |
Total Blocking Time (TBT-总阻塞时间) | 30% |
1、速度指数 Speed Index
「速度指数」(Speed Index)并不是一个具体的时间点,而是一个综合性指标,表示页面从加载开始到页面内容基本可见的过程中,用户感受到的加载速度。
得分范围 | 评价 |
---|---|
0 - 3.4秒 | 优秀 |
3.4 - 5.8秒 | 需改进 |
超过5.8秒 | 很差 |
2、首次内容绘制时间 First Contentful Paint
「首次内容绘制时间」(First Contentful Paint ),用来衡量从用户开始打开网页到浏览器渲染出第一块内容(如文本、图像、SVG等)的时间。
得分范围 | 评价 |
---|---|
0 - 1.8秒 | 优秀 |
1.8 - 3秒 | 需改进 |
超过3秒 | 很差 |
3、累积布局偏移 Cumulative Layout Shift
「累积布局偏移」(Cumulative Layout Shift),衡量网页在加载过程中视觉稳定性的一个性能指标
得分范围 | 评价 |
---|---|
0 - 0.1 | 优秀 |
0.1 - 0.25 | 需改进 |
超过0.25 | 很差 |
您是否曾经在页面上突然发生变化时在没有警告的情况下,文字移动了,并且您失去了位置。甚至更糟:您将要点击一个链接或一个按钮,但是在手指落下的瞬间,链接移动了,您最终单击了其他东西!
页面内容的意外移动通常是由于异步加载资源或将 DOM 元素动态添加到现有内容上方的页面而发生的。罪魁祸首可能是尺寸未知的图像或视频,呈现比其后备更大或更小的字体,或者是动态调整自身大小的第三方广告或小部件。
4、最大内容绘制 Largest Contentful Paint
「最大内容绘制」(Largest Contentful Paint,简称LCP)记录了页面中最大元素(可能是一张图像或者一段文本)显示在屏幕上所需的时间。这个指标的意义在于,当这个最大元素加载完成后,用户通常会认为页面基本上已经加载完成,即使背后可能还有其他的内容和脚本在继续加载和执行
得分范围 | 评价 |
---|---|
0 - 2.5秒 | 优秀 |
2.5 - 4秒 | 需改进 |
超过4秒 | 很差 |
LCP 考虑的元素:
< img > 元素< image > 元素内的 < svg > 元素< video > 元素(封面图)通过 url() 函数加载背景图片的元素包含文本节点或其他内联级文本元素子级的块级元素
5、总阻塞时间 TBT(Total Block Time)
Total Block Time(TBT)总阻塞时间,度量了 FCP 和 TTI 之间的总时间,在该时间范围内,主线程被阻塞足够长的时间以防止输入响应。
只要存在长任务,该主线程就会被视为“阻塞”,该任务在主线程上运行超过50毫秒(ms)。我们说主线程“被阻止”是因为浏览器无法中断正在进行的任务。因此,如果用户确实在较长的任务中间与页面进行交互,则浏览器必须等待任务完成才能响应。
如果任务足够长(例如,超过50毫秒的任何时间),则用户很可能会注意到延迟并感觉页面缓慢或过时。
给定的长任务的阻止时间是其持续时间超过50毫秒。页面的总阻塞时间是FCP和TTI之间发生的每个长任务的阻塞时间的总和。
例如,考虑页面加载期间浏览器主线程的下图:
上面的时间轴有五个任务,其中三个是长任务,因为它们的持续时间超过50毫秒。下图显示了每个长任务的阻塞时间:
因此,虽然在主线程上运行任务花费的总时间为560毫秒,但只有345毫秒的时间被视为阻塞时间。
参考文章:
浏览器之性能指标-LCP-腾讯云开发者社区-腾讯云 (tencent.com)
面试必问——前端页面性能指标基本介绍-腾讯云开发者社区-腾讯云 (tencent.com)
前端性能优化手册 - RoadMap-CSDN博客
[译]前端性能优化指标-累积布局偏移(CLS) - 掘金 (juejin.cn)
(99+ 封私信 / 80 条消息) Web项目可以从哪些方面进行性能优化? - 知乎 (zhihu.com)
前端性能优化_大前端_roadup_InfoQ写作社区
二、前端代码 性能优化实践
性能优化 主要 着重于 性能(Performance)进行优化,即:Total Blocking Time (TBT-总阻塞时间)、Largest Contentful Paint (LCP-最大内容绘制)、Cumulative Layout Shift (CLS-累积布局偏移)、First Contentful Paint (FCP-首次内容绘制)、Speed Index (速度指数):
根据 Lighthouse 10 规则,Web 前端性能指标主要有 5 个:
「性能指标」 | 「权重」 |
---|---|
Speed Index (速度指数) | 10% |
First Contentful Paint (FCP-首次内容绘制) | 10% |
Cumulative Layout Shift (CLS-累积布局偏移) | 25% |
Largest Contentful Paint (LCP-最大内容绘制) | 25% |
Total Blocking Time (TBT-总阻塞时间) | 30% |
一、现状:前端系统性能报告(多次测试取平均,开发环境)
根据报告 Performance 得分基本在 18左右,远未达到 90及以上,需要着重优化。
二、已采取的优化措施
再次进行优化之前,该前端代码已经采取了一些优秀且有效的、值得推荐的优化措施,具体如下。
1、使用 CDN
内容分发网络(Content Delivery Network,简称 CDN)是让用户从最近的服务器请求资源。提升网络请求的响应速度,同时减少应用打包体积,利用浏览器缓存,可以长期缓存不会变动的文件
2、打包时采用 GZIP 压缩
gzip 压缩可以特别明显的提高我们的代码加载效果,提升效率 5-6 倍左右,它会把诸如 js、css 等文件进行压缩,并且让我们在加载时去请求那些 gz 文件而提升请求效率。
3、路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
4、其他优化
精灵图、打包优化等其他相关优化措施
三、优化实践(开发环境)
(一)、按需引入
按需引入:在使用时才引入第三方库 或 全局引入时采用按需引入第三方库而不是全量引入
这里以 echarts 的优化为例。
第一种:真正使用时才引入(这里实践优化采用了此方式)
1、echarts 等 js
如下图所示,直接在 main.js 中引入 echarts,采用全局全量引入的方式,虽然方便了后续在 Vue 文件中可以直接使用 this.$echarts,但是却加重了首屏加载时的负担。因为此时的首屏界面加载时并没有用到 echarts。
因此,在具体的 Vue 文件中(真正使用到 echarts 的地方)再去引用,才是合理的。
而具体效果可以看到,同样的代码(同样屏蔽了很多其他代码),只是在 main.js 中是否引用 echarts(main.js 中不引用时,真正使用的 Vue 中引入了),性能效果对比如下:从 64 分提升到了 84分。
可以看到 main.js 中引入 echarts 前后,对于首屏加载的性能是有着明显的影响的。
同时也无需担心在多个 Vue 文件中多次引入 echarts(不同的 Vue 文件都使用了 echarts 图表),因为 js 的 import 机制决定了同一路径的第三方库的重复 import 并不会重复载入第三方库的 js 文件(第一次会载入第三方库的 js 文件,第二次及以后会读取缓存)。
2、components 等 vue
如下图,这里将 components 下的 CommonExtent、FeatureResult等使用了 Cesium.js(Space) 开源库的 Vue 组件 放置到了 widgets 文件夹下。
因为 widgets 文件夹下的组件、功能是可以动态引入的(真正使用时才会 import 导入)。
这样就避免了main.js 引入 components 时,会直接加载 Cesium.js(Space)相关的 js 文件,从而降低首屏加载的js文件的数量,提升性能。
参考文章:
javascript - 当一个模块被导入两次时,会发生什么? - 疯狂的技术宅 - SegmentFault 思否
讨论 JS 的 import 工作机制,多次 import 是否会产生同名不同实例?是否和 Python 一致 - Juwan - 博客园 (cnblogs.com)
import - JavaScript | MDN (mozilla.org)
彻底理解JavaScript ES6中的import和export - 问上 - 博客园 (cnblogs.com)
javascript学习笔记 - import存在重复引用js问题么(附详细import说明)_js import from 方法名重复-CSDN博客
第二种:全局引入时采用按需引入
如果需要全局引入,也建议采用按需引入的方式,如下代码所示:
<code>// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from "echarts/core";
/** 引入柱状图and折线图图表,图表后缀都为 Chart */
import { BarChart, LineChart } from "echarts/charts";
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
} from "echarts/components";
// 标签自动布局,全局过渡动画等特性
import { LabelLayout, UniversalTransition } from "echarts/features";
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from "echarts/renderers";
// 注册必须的组件
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
BarChart,
LabelLayout,
UniversalTransition,
CanvasRenderer,
LineChart,
]);
// 导出
export default echarts;
参考文章:
Vue中按需引入ECharts(一看就会)_echarts按需引入-CSDN博客
(二)、动态引入
动态引入:动态 import()
1、直接引用的动态引入:css、svg 等
当 main.js 中必须引入一些组件、样式时,可以采用动态 import() 的方式去动态加载模块。
即不是采用 import from “animate.css” 这种方式,而是采用 import(“animate.css”) 的动态加载的方式。
可以看到,只是改变了几个 css、components、svg 的 import 的方式(从 import 改为 import() ),性能报告的分数也有约4%的提升。
而对于需要在 main.js 中引入较多的 JS库、CSS、组件时,可以采用 Promise.all 的方式,如下:
参考文章:
23. Module 的语法 - import() - 《阮一峰 ECMAScript 6 (ES6) 标准入门教程 第三版》 - 书栈网 · BookStack
动态 import & top-level await - 简书 (jianshu.com)
一文搞懂 Dynamic Import 和 Top-level await 提案 · Issue #38 · yinguangyao/blog · GitHub
高级特性: 动态导入(Import) | Next.js | Next.js中文网 (nextjs.cn)
在多个文件中import同一个文件,webpack会多次打包吗 - 掘金 (juejin.cn)
2、间接引用的动态引入:Cesium.js、Space 等
在 main.js 中引入了 vuex,而vuex 引入的模块中使用了可延时引入的三方库。
相当于 main.js 中间接引用了三方库。
因此可以将main.js 中直接引用的JS 中 涉及到的间接引用的、可延时、动态引入的三方库 从 顶层的 import…from… 引入方式,改为 await import() 动态引入方式。
这样就可以保证在 main.js 运行时(首屏加载运行时),不会直接加载可延时的 JS 三方库,从而提升首屏加载效率。
Ⅰ、main.js 中直接引入的 JS 文件中 引用了其他可延时的三方库,均改为动态引入
(三)、压缩资源
压缩资源:图片使用 WebP或AVIF
将 jpg、png 等图片转为 WebP(目前 WebP 浏览器兼容性比 AVIF 好,而 AVIF 压缩的图片比 WebP 更小)。
图片转 webp
可以看到上述 svg 文件从原来的 664KB 缩小到了 31KB,文件大小缩小了95.33%,html 文件从原来的 138KB 缩小到了 81KB,文件大小下降了41.30%。
参考文章:
javascript - 为什么在2024年应该使用AVIF而不是JPEG、WebP、PNG和GIF - 终身学习者 - SegmentFault 思否
使用现代图片格式:AVIF和webP - 掘金 (juejin.cn)
(四)、减少及优化网络请求
减少及优化网络请求:只请求首屏加载需要的请求,数据量较大的JSON、文件等采用流式请求
这里首屏加载时请求了所有页面(包括系统加载后,其他需要点击才会切换的页面)的后台数据(用于渲染所需的数据)。然而首屏加载时仅需要首屏界面上所需要的数据,并不需要将所有页面的数据都请求到,后续需要点击切换而显示的界面,则可以在点击时再进行动态的数据请求以支持渲染。从而可以减少首屏加载的网络请求数量,提升首屏速度。
这里主要采用了两种方式来提速:
1、非首屏的网络请求予以剔除,点击切换界面时再进行动态请求
2、首屏所必须的网络请求采用 asyncPool 进行并发控制请求,而不是逐个请求
Ⅰ、减少首屏的网络请求数量,只请求当前首屏加载需要的请求
① 第一次优化:将 while 循环中的逐个异步请求,更改为 while 循环下的并发异步请求
可以看到原来的方法使用 while 循环,while 循环内部调用了耗时异步操作。
新的方法同样采用 while 循环,但是针对于 Resource 资源的请求采用了 asyncPool 并发方法,通过有序索引,可以保证有序、有限的并发请求,通过有序并发提升请求速度。
可以看到原方法基本都在10s以上才能走完所有 while的请求,而新的并发方法则可以将耗时缩短至4s以下。
以下是用同一个方法进行了2次测试,得到的结果:
但上面的做法只是将逐个的网络请求优化为了并发的网络请求,并没有减少网络请求的数量。
②第二次优化:减少网络请求的数量
首屏进入时,当前代码逻辑是请求了所有的组件的 detail 信息,包括通统计分析、对比分析、空间查询等等未首次展示的界面下的所有组件信息的 detail。但这里并没有必要一次性、递归请求所有的,未直接在首屏展示的界面、组件不应该进行请求,而是当进行点击时再去动态请求。
这样就可以极大减少首屏进入时的网络请求数量。
优化思路:
1、修改 … .vue 中 调用的 register…ByTree 方法(该方法请求了系统初始化的资源信息),根据配置信息,判断是否要在首屏进入时进行网络请求;非必要的请求都在后续交互界面触发时动态请求
**2、全局保存首屏进入时没有注册的资源,后续动态注册后,再逐一动态删除:从而实现除了首屏外的资源,都是用户交互了之后才会注册,且注册后不会再反复注册 **
**3、动态注册逻辑 **
在鼠标点击触发路由打开时,首先判断该路由资源是否已经注册(如果是首屏的路由,已经注册过,就不会重复注册),未注册过的,就会再此时动态注册,注册完成后,store 中记录的未注册的资源就会剔除当前注册的资源。从而保证已经注册的不会重复注册。
4、效果
可以看到,优化后,首屏对于detail 的接口的网络请求数量下降了 46.75%,网络请求的总耗时大幅降低了75%。
同时可以看到,优化前首屏界面所有元素显示完全需要15s-16s左右,而优化后,则只需要5s-6s左右。开发环境下,首屏页面完全渲染完成的时间缩短了 62%~66% ,速度快了 166.67% ~ 199.99%
参考文章:
[前端性能优化之——浏览器http请求并发_前端 并发请求是什么意思-CSDN博客](https://blog.csdn.net/weixin_43236062/article/details/121988827#:~:text=同一域名下,同一GET请求的并发数是1,也就是说上一个请求结束,才会执行下一个请求,否则置入队列等待发送;,同一域名下,不同GET%2FPOST请求的并发数量是6。 当发送的请求数量达到6个,并且都没有得到响应时,后面的请求会置入队列等待发送。)
你知道浏览器并发请求的最大数量和机制吗? - 掘金 (juejin.cn)
async-pool实现控制并发请求 - 掘金 (juejin.cn)
Ⅱ、采用 fetch 的 流的方式 处理后台返回的较大的 JSON、文件等数据
steam 请求方式,页面会边下边解析。数据逐步加载的,几乎没有白屏时间。
流应该是个很常见的概念,它允许我们一段一段地接收与处理数据。相比较于获取整个数据再处理,流不仅不需要占用一大块内存空间来存放整个数据,节省内存占用空间,而且还能实时地对数据进行处理,不需要等待整个数据获取完毕,从而缩短整个操作的耗时。
Streams API 赋予了网络请求以片段处理数据的能力,过去我们使用 <code>XMLHttpRequest 获取一个文件时,我们必须等待浏览器下载完整的文件,等待浏览器处理成我们需要的格式,收到所有的数据后才能处理它。现在有了流,我们可以以 TypedArray
片段的形式接收一部分二进制数据,然后直接对数据进行处理,这就有点像是浏览器内部接收并处理数据的逻辑。甚至我们可以将一些操作以流的形式封装,再用管道把多个流连接起来,管道的另一端就是最终处理好的数据。
使用流 和 不使用流 的区别
这里对一个获取列表(List)的功能进行了优化(改用 fetch 的 流 来处理),可以看到分别进行五次测试后,未使用流之前平均耗时410ms,而优化后平均耗时 70ms,耗时缩短了82.93%。
优化前耗时:测试五次
|
优化后耗时:测试五次
|
而从 UI 显示上来看,优化后,列表几乎是立即显示的,而优化前会有短暂的白屏。这里为了对比统一对测试效果的录屏进行了降速,以0.4倍速的速度进行演示对比视频。
上图为某一时刻,优化前后,界面渲染的对比,可以看到,优化前的白屏时间会更长一些。
需要注意的是,使用 fetch 的 流 方式进行请求,需要针对不同的后端返回的数据类型进行处理,上面是对数组类型进行的处理。具体来说:
比如以下接口放回的数据,原本 JSON 格式,使用流 之后 通过 解析为字符串,然后进行字符串基础上的列表解析,逐个返回,实现快速界面接收、渲染。
具体代码逻辑为:
通过以上核心逻辑的三个步骤,可以直接将获取到的逐个或多个 Item 立即传入到 Vue 的 Data 中,进而绑定 Data 的界面会逐个、立即更新;这样基于流的方式,就不用像之前的方法一样,必须等待List 中的所有 Item 拿到后才渲染,进而提升渲染速度。
参考文章:
从 Fetch 到 Streams —— 以流的角度处理网络请求 - 知乎 (zhihu.com)
利用 Generator 和 Fetch 对 json 数据流 stream 进行边下载边解析_fetch json-CSDN博客
[fetch实现流式输出的实现原理_fetch nodejs 客户端 流式输出-CSDN博客](https://blog.csdn.net/jyl4855/article/details/136484042#:~:text=Fetch API 的一个强大功能就是能够处理流式响应,即服务器可以分块发送数据,而不是一次性发送整个文件,这样浏览器就可以立即开始显示接收到的数据,而不需要等待整个文件下载完毕。 要实现 fetch 的流式输出,关键在于如何正确地处理返回的 ReadableStream 对象。,对象的 body 属性就是一个 ReadableStream 实例。 为了实现流式输出,可以使用 TransformStream 类来处理数据流。)
Fetch API - stream 数据流 - 《阮一峰 Web API 教程》 - 书栈网 · BookStack
fetch获取流式数据相关问题 - 掘金 (juejin.cn)
fetch获取text/event-stream数据并处理_fetch eventstream-CSDN博客
(五)、避免在 Router 中直接载入大量 SVG等资源
可以看到初始化时,初始化了 router,而 router 中 通过 isDev() 判断使用了 SvgViewer 组件,而 SvgViewer 组件读取了 svg 文件夹下的所有 svg 文件,这将使得系统初始化加载时性能下降。而且 Development 环境下也没有必要使用 SvgViewer(虽然它提供了可以直接点击获取 svg 源码的功能)。
而且main.js 中已经使用了 import(“@/svg”)。
因此这里删除 router 中对于 SvgViewer 的使用以及直接剔除 SvgViewer 文件(因为开发环境没有必要使用,生产环境也没有用到)
验证效果:
剔除后,再次运行测试报告,屏蔽了同样的代码后,只是 Router 中是否引入 SvgViewer 区别,对比结果如下:
可以看到,性能分数直接来到了80左右,性能分数提升了25%。
(六)、Vue 中 初始化数据/网络请求 放在 Created 中
接口请求可以放在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
能更快获取到服务端数据,减少页面 loading 时间SSR 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于代码的一致性created 是在模板渲染成 html 前调用,即通常初始化某些属性值,然后再渲染成视图。如果在 mounted 钩子函数中请求数据可能导致页面闪屏问题
<code>created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
beforeMount
发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted
在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
因此对于 Vue 初始化时,非 Dom 操作的初始化(数据请求、网络请求等)均放在 Created 中。
参考文章:
Vue请求是在Created还是Mounted? - 掘金 (juejin.cn)
Vue中在哪个生命周期内调用异步请求?_vue一般在哪个生命周期请求异步数据-CSDN博客
(Vue)优化Vue.js异步数据请求的最佳实践:何时调用? - 掘金 (juejin.cn)
(七)待验证的可优化点(优先级不分先后)
1、使用 HTTP2
Chrome浏览器,默认会限制6个文件数量的并发请求。因此如果你有12个文件,针对 http 1.0 协议,就会分成2个批次去请求资源;如果是http2.0,就会同时请求这12个文件
参考文章:
使用 HTTP/2 提升性能的几个建议-腾讯云开发者社区-腾讯云 (tencent.com)
前端性能优化(三)聊聊HTTP/2带来的加载优化 - 知乎 (zhihu.com)
2、生成 关键 CSS 及 内联首屏关键CSS
关键CSS即页面第一次可见部分的CSS(或称首屏显示的CSS),通常内联到html的中。由于缓存,将关键的CSS(和其他重要资源)放在根域的单独文件中有时甚至比内联有很多好处。注意:使用HTTP / 2时,关键CSS可以存储在单独的CSS文件中,并且可以通过服务器推送来传递,而不会膨胀HTML。问题在于,服务器推送存在许多浏览器之间的陷阱和竞争条件,因此很麻烦。
我们可以使用criticalCSS和critical生成关键CSS,使用webpack的插件critters实现内联关键css,懒加载剩下的。
如果你还在使用loadCss异步加载你的所有css,那就没必要了。使用media="print"可以欺骗浏览器异步加载CSS,只要加载完成,就立即应用在屏幕环境。
性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间,而 内联首屏关键CSS(即Critical CSS,可以称之为首屏关键CSS) 能减少这一时间。
很多人都喜欢通过<code>link标签引用外部CSS文件
。但需要知道的是,将CSS直接内联到HTML文档中能使CSS更快速地下载。而使用外部CSS文件时,需要在HTML文档下载完成后才知道所要引用的CSS文件,然后才下载它们。所以说,内联CSS能够使浏览器开始页面渲染的时间提前,因为在HTML下载完成之后就能渲染了。
但是我们不应该将所有的CSS都内联在HTML文档中,因为[初始拥塞窗口]存在限制(TCP相关概念,通常是 14.6kB,压缩后大小)
,如果内联CSS后的文件超出了这一限制,系统就需要在服务器和浏览器之间进行更多次的往返,这样并不能提前页面渲染时间。因此,我们应当只将渲染首屏内容所需的关键CSS内联到HTML中。
⚠️还有一点需要注意的是内联CSS没有缓存,每次都会随HTML的加载而重新下载,但我们将内联首屏关键CSS控制在 14.6kB
以内,它对性能优化还是起到正向作用的。(凡事有利也有弊)
参考文章:
前端性能优化(四):传输优化 - 个人文章 - SegmentFault 思否
CSS性能优化的几个技巧 - 前端南玖 - 博客园 (cnblogs.com)
3、响应式优化
Vue 的响应式系统是一个强大特性,但也会产生性能开销。通过以下方案,我们可提升其效率:
减少响应式对象数量: 只将需要响应的属性置于 Vue 实例数据(Data)中。
使用缓存: 对于频繁访问的数据,可通过缓存减少响应式系统查询。
const cachedData = Vue.observable({
items: []
});
function fetchItems() {
// 从服务器获取项目
// ...
cachedData.items = items;
}
使用非响应式属性: 对于不需要响应的属性,可使用 Vue.set() 方法将其标记为非响应式。
参考文章:
九种Vue性能优化方法 - 简书 (jianshu.com)
最全 Vue 性能优化方案_vue性能优化-CSDN博客
Vue 直达性能优化:三招教你搞定响应式、渲染和数据流 - ByteZoneX社区
4、数据流优化
优化 Vue 数据流的方法如下:
避免不必要的更新: 仅在数据发生实际变更时更新数据。
使用异步更新: 对于不紧急的数据更新,可使用 Vue.nextTick()
方法将其推迟到下一次事件循环中进行。
使用 lodash 或其他库进行函数节流和去抖: 对于需要频繁触发的函数,可使用这些库中的功能减少不必要的函数调用。
const debouncedUpdate = _.debounce(() => {
// 更新数据
}, 500);
// 每 500 毫秒调用一次更新函数
@debounceUpdate
onDataChange() {
// ...
}
5、CSS 遵循 BEM 规范和 原子化规范,尽量不要嵌套
浏览器的CSS解析器解析css文件时,对CSS规则是从右到左匹配查找,如果选择器嵌套层数太多会造成CSS Tree加载变慢、影响回流重绘效率,最终影响渲染速度。
一般情况下,元素的嵌套层级不能超过3级,尽量保持简单,不要使用嵌套过多过于复杂的选择器。
参考文章:
『精』CSS 小技巧之BEM规范_css bem-CSDN博客
深入理解 BEM:前端开发中的命名约定革命_bem,也就是 “block-element-modifier”,-CSDN博客
CSS的革命:从基础到前沿的全面演变 - 知乎 (zhihu.com)
CSS - BEM和原子CSS - Brandon布兰登 - 博客园 (cnblogs.com)
「译」CSS语义化是怎么往原子化进化的? - 掘金 (juejin.cn)
css选择器一般不建议嵌套3层,为什么? - 掘金 (juejin.cn)
CSS性能优化的几个技巧 - 前端南玖 - 博客园 (cnblogs.com)
6、CSS 避免使用 @import
不建议使用@import
主要有以下两点原因:
使用@import
引入CSS会影响浏览器的并行下载。使用@import
引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。这就导致浏览器无法并行下载所需的样式文件。多个@impor
t会导致下载顺序紊乱。在IE中,@import
会引发资源文件的下载顺序被打乱,即排列在@import后面的js文件先于@import下载,并且打乱甚至破坏@import自身的并行下载。
7、尽可能按需引入
在项目中尽可能避免 *
导入全部而是使用按需导入,否则就会导入很多我们用不到的东西从而影响项目打包的体积大小以及页面加载速度。
8、销毁资源
使用完某些代码后一定要销毁资源,比如定时器。一般在 beforeDestroy
中销毁。这样可以避免资源浪费以及内存泄露
9、后台接口优化
前端性能的优化还依赖于后端接口,所以后端接口的性能也直接决定了前端系统的性能。基于当前前端系统的代码逻辑,后台接口可优化的地方为:
10、Cesium 的 CSS 引入优化
使用 Cesium 时,一般会直接引入 widgets.css,而 widgets.css 中引入了较多的 css 及其相关的各类资源,而实际场景中,并不完全需要所有的 css,因此可以尝试如下图所示的例子,屏蔽不需要的 css,从而减少资源在地图初始化时的加载,进而提升性能。
11、其他
其他一切可优化的措施
参考文章:
最全 Vue 性能优化方案_vue性能优化-CSDN博客
[转][浏览器GC]深入理解JavaScript垃圾回收 | Vue.js | Vue.js 技术论坛 (learnku.com)
避免内存泄漏 — Vue.js (vuejs.org)
一文让你彻底搞懂JS垃圾回收机制 - 掘金 (juejin.cn)
参考文章:
如何将 Lighthouse Performance 评分从 20 提高到 96 - 掘金 (juejin.cn)
[前端性能优化——启用文本压缩 - 飞仔FeiZai - 博客园 (cnblogs.com)](https://www.cnblogs.com/yuzhihui/p/17211727.html#:~:text=前端性能优化——启用文本压缩 1 1、通过 Google Chrome 打开需要进行性能优化的站点 2 2、打开,Lighthouse 面板: 3 3、在 Lighthouse 面板中根据自己的需求自定义分析项和分析配置,点击”分析网页加载情况“对页面进行性能等方面的分析: 4 4、分析完成后如图所示:)
reduce javascript execution time_mob64ca12f1c6f8的技术博客_51CTO博客
【第1501期】浏览器往返缓存(Back/Forward cache)问题的分析与解决_搜狐汽车_搜狐网 (sohu.com)
动态加载与异步加载 JavaScript 详解:加载远程js,加载成功后执行回调函数-阿里云开发者社区 (aliyun.com)
Web性能监测的利器Performance Observer!! - 掘金 (juejin.cn)
vue 里面引入外部js的问题 和js文件加载顺序的问题(元素还没有被渲染到页面上,js获取不到该元素) - 王二疯 - 博客园 (cnblogs.com)
web - 前端性能优化——关键渲染路径 - 不挑食的程序员 - SegmentFault 思否
超级详细的手把手教你使用Lighthouse更好推动项目性能优化,性能指标详解,优化方法,需要关注指标分析-CSDN博客
项目上线如何打包优化? - 掘金 (juejin.cn)
九种Vue性能优化方法 - 简书 (jianshu.com)
Vite配置如何优雅的code spliiting代码分割 - 掘金 (juejin.cn)
前端性能优化(四):传输优化 - 个人文章 - SegmentFault 思否
Web 性能优化:缩短 Content download,提升页面响应速度-腾讯云开发者社区-腾讯云 (tencent.com)
Fetch Priority与LCP优化:提升网页加载速度的关键 (baidu.com)
前端加载优化小技巧 —— 使用fetchpriority=high属性为页面最大资源的加载提速 - 知乎 (zhihu.com)
[前端优化]项目优化–Lighthouse_如何根据lighthouse的性能结果优化项目代码-CSDN博客
webp to base64 网站:WebP to Base64 | Image | Base64 Encode | Base64 Converter | Base64
png to webp 网站:PNG 到 WEBP 在线转换器,免费 PNG 到 WEBP 转换 🔄 (imageonline.co)
一个可以做性能优化的 css 属性: content-visibility (qq.com)
CSS 兼容性网站:Can I use… Support tables for HTML5, CSS3, etc
JavaScript 性能提升:Web Worker 的妙用-百度开发者中心 (baidu.com)
web性能优化(Lighthouse和performance):从实际项目入手,如何监测性能问题、如何解决。 - 掘金 (juejin.cn)
什么是 BFCache(前进/后退缓存) - 掘金 (juejin.cn)
Webpack揭秘——走向高阶前端的必经之路 - 掘金 (juejin.cn)
路由懒加载 | Vue Router (vuejs.org)
前端加载优化实战,减少页面渲染时间66%_大前端_张海超_InfoQ精选文章
低延迟系统的 11 个最佳实践_51CTO博客_低延迟系统开发
Web前端性能优化——如何提高页面加载速度 - FEDeveloper - 博客园 (cnblogs.com)
四、开发环境优化前后性能对比
优化前:
优化后:
对比结果:
指标 | 优化前分数 | 优化后分数 | 性能提升 |
---|---|---|---|
Performance | 18 | 28 | 55.56% ⬆️ |
Accessibility | 90 | 91 | 1.11% ⬆️ |
Best Practices | 52 | 59 | 13.46% ⬆️ |
SEO | 78 | 80 | 2.56% ⬆️ |
可以看到开发环境下,优化后各类指标分数都有所提升,Performance 提升了 55.56%,Accessibility 提升了 1.11%,Best Parctices 提升了 13.46%,SEO 提升了 2.56%。
五、生产环境优化前后性能对比
优化前:
优化后:
对比结果:
指标 | 优化前分数 | 优化后分数 | 性能提升 |
---|---|---|---|
Performance | 28 | 48 | 71.43% ⬆️ |
Accessibility | 86 | 86 | 0% |
Best Practices | 61 | 61 | 0% |
SEO | 80 | 80 | 0% |
可以看到优化后,生产环境下 Performance 提升了 71.43%,性能分数提升显著。
同时可以看到,优化前首屏界面所有元素显示完全需要10s左右,而优化后,则只需要6s左右。生产环境下,首屏页面完全渲染完成的时间缩短了 40% ,速度快了 66.67%
而以上的对比还是在浏览器的隐私窗口(无缓存)模式下,还并未发挥 CDN 等的缓存优化措施的作用。
总结
这里使用 LightHouse 的目的,是为了落地并实践出一套基本完整的前端性能验证、优化的路径,为后续各个前端项目的性能优化提供思路及参考。
本次性能优化的实践,一方面验证了规范化的代码习惯、逻辑的重要性。使得开发人员认知到遵守开发规范对于性能的影响;另一方面验证了不同优化方式,对于性能的提升的影响。使得开发人员了解如何做性能优化。从而拓展开发团队在性能优化方面的意识、能力。
首先该前端框架本身使用了很多第三方库及其资源,其次该前端框架使用了 Cesium.js 等重型的地图库,性能优化及提升有很多点可以去做,但也很难做。
这里使用 LightHouse 对前端系统进行评分,理想情况下自然是每一项指标都能取得良好的分数,但在目前前端框架的基础上实现当前情况下的性能提升及评分,实属不易。
当然,还有很多优化点值得去做,每个指标都值得分数的再提高。仍需继续将待验证的可优化点进行优化后的验证,以及探索其他可能的优化措施!
此外,每个前端系统都有通用的优化方法,当然也有每个系统单独的优化方式。毕竟不同的系统的实现逻辑、使用资源的方式都不尽相同!
而以上的对比还是在浏览器的隐私窗口(无缓存)模式下,还并未发挥 CDN 等的缓存优化措施的作用。
总结
这里使用 LightHouse 的目的,是为了落地并实践出一套基本完整的前端性能验证、优化的路径,为后续各个前端项目的性能优化提供思路及参考。
本次性能优化的实践,一方面验证了规范化的代码习惯、逻辑的重要性。使得开发人员认知到遵守开发规范对于性能的影响;另一方面验证了不同优化方式,对于性能的提升的影响。使得开发人员了解如何做性能优化。从而拓展开发团队在性能优化方面的意识、能力。
首先该前端框架本身使用了很多第三方库及其资源,其次该前端框架使用了 Cesium.js 等重型的地图库,性能优化及提升有很多点可以去做,但也很难做。
这里使用 LightHouse 对前端系统进行评分,理想情况下自然是每一项指标都能取得良好的分数,但在目前前端框架的基础上实现当前情况下的性能提升及评分,实属不易。
当然,还有很多优化点值得去做,每个指标都值得分数的再提高。仍需继续将待验证的可优化点进行优化后的验证,以及探索其他可能的优化措施!
此外,每个前端系统都有通用的优化方法,当然也有每个系统单独的优化方式。毕竟不同的系统的实现逻辑、使用资源的方式都不尽相同!
上一篇: ❤ 若依框架vue2版本(springboot-vue前后分离--前端部分-全面解析)
本文标签
这么做就对了~:基于 LightHouse 的前端性能评价及代码优化实践流程 什么?Cesium.js 三维前端系统 性能提升 71.43%!首屏速度提升 200%!—— 前端性能优化
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。