首页 > 其他分享 >PC首页资源加载速度由8s降到2s的优化实践

PC首页资源加载速度由8s降到2s的优化实践

时间:2023-09-11 12:34:30浏览次数:42  
标签:2s cdn js PC 8s 加载 优化 资源 页面

https://www.cnblogs.com/vigourice/p/17689990.html

 

随着需求的不断开发,前端项目不断膨胀,业务提出:你们的首页加载也太慢啦,我都需要7、8秒才能看到内容,于是乎主管就让我联合后端开启优化专项,目标是3s内展示完全首页的内容。

性能指标

开启优化时,我们要清晰的知道现状和目标,以及我们采用什么样的手段,通过检测什么指标来查看到优化的过程。

结果指标

根据这个目标,我们可以选择一些性能指标,google 提供了基于用户体验的性能指标,如FCP、LCP、FID、TTI、TBT、CLS等,也有指标更少新的用户体验量化方式 Web Vitals,只选取 LCP、FID、CLS。

我们这次主要选择的指标是 FCPLCP。FCP 表示着用户能最快看到页面内容的时间,LCP 则是可视区域的最大内容,这两个指标代表着用户真实对于页面快和慢的体验,FCP 在 1.8 秒内,LCP 在2.5 秒内是比较好的。

通过 chrome 浏览器 lighthouse 功能可查看的当前页面的结果指标数据,该项目首页 LCP 和 FCP 时间分别为 7.5s、1s,LCP 非常之慢了。

过程指标

FCPLCP 是希望达到的结果,在优化的过程中,我们需要一些数据来记录到底在哪些方向做的优化能导致一个比较好的结果,比如请求数量、页面加载时间、打包总体积/入口文件体积、依赖的 cdn 数量、传输资源体积。

无痕模式下通过 chrome 开发者工具 network 记录这些过程指标,当前项目首页过程指标:请求(requests) 117 个,页面加载时间(Load)3.79 秒,打包总体积(resources) 21.8MB、依赖的 CDN(需要自己点击页面右键查看网页源码去数)25个js 、7个css资源,传输资源体积(transferred)6.6MB

较多的资源数量、较大的打包体积都导致了页面加载速度变慢。

页面生命周期

首先我们得知道页面的生命周期,才能有针对性的去对前端参与的过程进行优化。那么从浏览器地址栏输入url,到页面渲染出来,主要经过了哪些步骤呢?

  • 输入域名后,DNS 域名解析
  • 发起TCP的3次握手
  • 建立TCP连接后发起 http 请求
  • 服务器端响应http请求,浏览器得到html代码
  • 浏览器解析html代码,并请求html代码中的资源
  • 浏览器对页面进行渲染呈现给用户(DOM、CSSOM、渲染树)

在这个过程中,有很多网络、服务器参与的过程,而我们主要关注前端能够进行的优化,比如使用 DNS 预解析、缓存机制、减少项目编译后 html / css/ js 包资源大小。

通用优化

删除无用代码

这一步适用于所有项目,定期的清理掉不需要的代码能避免项目无止境的膨胀。因我们项目的历史遗留问题,存在一些无用的cdn资源、依赖、组件、配置,在页面加载的过程中他们占据了一定的网络带宽。

无用 cdn 资源

当前业务场景下这些资源都已经用不到,也无需在页面初始化的时候加载。

功能重复的资源

由不同开发者根据集团规范引入了,两个不同域名但相同功能的前端异常数据采集的 cdn 资源,只需保留一个仍然维护并且推荐使用的即可。

无用依赖

找遍整个项目都用不到的依赖

无用请求逻辑

为了测试组件,跟随首页发送的请求,但因业务场景不太相符,未真正投入使用

无用文件

可用webpack插件 unused-files-webpack-plugin 找到已不需要使用的文件,再通过 nodejs 中内置的文件处理 fs 定义函数将其递归删除。

网络相关

域名收敛

在项目中,我们可能需要加载很多不同的内容,无论是了为了页面更为美观的图片、字体,还是不适合编译到项目中体积较大的cdn的资源(如 echarts、谷歌地图)。

我们知道通过 http 请求获取资源需要经历 DNS 域名解析、TCP 三次握手、建立 TCP 连接后发送 http 请求、服务器响应 .... 在这里第一步就是域名解析,如果本地没有缓存,那么这里将会从顶级域名开始一层层往下找,需要耗费很多时间。

通过域名收敛,将相同的应用/资源整合到同一域名,减少不同域名之间 DNS 的解析时间,我们项目资源主要有几大类(xxx代表公司域名)

  • JS、CSS 使用 g.alicdn.comassets.xxx.cn
  • 图片收敛到文件平台 imgcdn.xxx.cnassets.xxx.cn
  • 字体图标收敛到 at.alicdn.com
  • 特殊如高德地图 webapi.amap.com 之类

DNS预解析

在执行 js 文件前会存在一些空闲时间,可以用来解析 dns 地址,dns 预解析是异步执行的,不会对 html 页面代码造成阻塞,这样在真正加载资源时可以减少用户的等待时间。

上面已经将域名进行了收敛,html 中 元素通过 dns- prefetch 的 rel 属性提供这项功能,然后在 href 属性中指定要跨域的域名(仅作用于与当前页面不一样的域名)。

<link rel="dns-prefetch" href= "//imgcdn.xxx.com">

以上两个步骤分别删除无用的资源和提升网络连接,和项目类型、业务场景关联不大,属于很多项目都通用的方案。接下来的步骤需要根据资源编译情况来进行优化。

编译产物优化

在 webpack 配置中增加插件 webpack-analyzer-plugin,本地运行项目,编译后的资源包情况将在在 8888 端口展示。从编译产物中,我们可以看到每个 js 包原始大小,压缩后尺寸,js中主要有哪些库,根据这些具体的信息进行优化。

图中 vendor.js 是项目打包的入口文件,是第一次打开任意页面都会加载的资源,仔细观察那些体积较大的资源,想办法进行优化。

合并 echarts 版本

从图中可以看到,绘图组件有两个组件库,BizCharts 、echarts,而 echarts 还分为 5.4 和 4.9 的版本,并且在 index.html 中还通过 cdn 引入了 4.2.1 版本并且没有使用 externals排除打包。

找到不同 echarts 版本的组件来源,我们项目中两个版本分别来自 echarts 自身和公司业务组件,还有 react-for-echarts 组件的依赖 。

优化方案是找一个各组件都可以使用版本,将 package.json 和 cdn地址中echarts版本与组件库中保持一致(使用 5.3.3版本),通过 externals 排除打包,这样直接使用 cdn 资源,不再将 echarts 编译至入口文件 vendor.js 中

// index.html
"externals": {
    "echarts": "echarts"
}

另外还有 BizCharts,因为使用到的菜单已下线,所以直接将这部分删除。

loadash 工具缩包

项目中常会用到 loadash 之类的工具库,为了使用几个函数将整个工具库的资源都引入非常的不值得,比较好的方式是只通过 import 引入具体需要使用的文件,如 import _add from 'lodash/fp/add'

但很有可能并不是项目中的每个人都会遵守这样的规则,如果使用全量引入的方式,如 import _ from 'lodash', 导致全量资源被编译至项目主入口文件中。

为了避免这种情况,我们可以使用 babel 插件来 babel-plugin-lodash 将全量引入方式编译成按文件引入。

import _ from 'lodash'
import { add } from 'lodash/fp'

const addOne = add(1)
_.map([1, 2, 3], addOne)

编译成

import _add from 'lodash/fp/add'
import _map from 'lodash/map'

const addOne = _add(1)
_map([1, 2, 3], addOne)

babel-plugin-lodash 是 babel 插件, 在 webpack 中配置到 babel-loader

rules: [
      {
        exclude: /node_modules/,
        test: /\.js$/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['lodash'],
          },
        }],
      },
    ],

我们项目使用的是集团统一脚手架,只暴露出 webpack-chain 的方式去修改,所以是这样使用。

const merge = require('lodash/merge');
 config.module
  .rule('jsx')
  .use('babel-loader')
  .tap((options) => {
    return merge(options, {
      plugins: ['babel-plugin-lodash'],
    });
  });

从图中可以看出 xlsx 资源编译后的体积也非常大,但我们项目当前业务场景已没有使用到,所以直接删除,像低频使用的资源,可以替换成 cdn 链接或者拆包编译并延迟引入。

通过以上删除、合并整理,主入口资源从 7.51M 降低 2.51 M。

devtool

借助 chrome 浏览器的 devtool 来进行进行进一步的处理

lighthouse 建议

将项目打开在 chrome 浏览器中,使用 lighthouse 不仅可以用来检测项目 LCP、FCP评分,还有一些针对当前检测页面的建议。

点开之后就是每一条具体的建议,比如提示存在一些首页用不到的 js、css 资源,为图片增加宽高减少布局偏移。

首页用不到但其它地方需要使用 js、css 资源可以去除入口 index.html 的引入,改为到指定文件需要使用时再延迟加载 cdn 资源。

方案大概是这样:

  • 如果指定页面没有加载需要的资源,需要通过 scrpt 标签加载需要的 js 资源,link 标签加载需要的 css 资源
  • 判断 cdn 引入的资源已挂载在 window 对象之后,再开始执行页面的渲染

这样每个页面负责自己所需要的资源,无需将所有资源的加载压力都放到首页

network 查看接口响应

性能优化也不是前端努力就足够的,页面加载过程中,后端接口响应速度也很关键,如果后端接口响应过慢,前端拿不到数据也无法进行渲染。

在我们项目首页中,获取用户菜单权限的接口非常的慢,每次打开页面都需要一秒钟左右才能响应,存在很严重的阻塞问题,反馈给后端同学进行优化后,能保持在 100-200毫秒完成响应。

优化结果

通过以上优化,性能过程指标,load 时长、资源体积、依赖cdn数量、传输资源体积都有了很大的提升。

性能结果指标 LCP 保持在 1秒,FCP 由 7.5 秒降低至 1.1 秒,页面渲染完成由 8秒 降到 2秒左右,达到了我的预期。

优化完还有非常重要的事情就是 一定要充分测试! 已上线的需求在进行如此大的改动后,一定要先自测一遍业务功能是否受影响,再提给测试同学排期对整个项目进行测试,等到充分测试完成才能发布。

总结

攻城容易守城难,做好了一次优化不意味着能够长期的保持,重要的是全组的同学在平时的需求开发中注意性能做好维护工作,不然资源会像滚雪球一样越来越大,页面的加载速度就在无形之中越来越慢。

标签:2s,cdn,js,PC,8s,加载,优化,资源,页面
From: https://www.cnblogs.com/chinasoft/p/17693202.html

相关文章

  • 2020ICPC 区域赛南京
    ABCDEFGHIJ使用势能线段树维护区间xor和,现在就变成了求有多少个i满足xor[l,r]s[i]t=0挪一挪变成xor[l,r]^s[i]<s[i]这个等价于看xor[l,r]的第一位是不是被s[i]包含,于是维护每个数每位的信息即可两个logKk=0无解。k=1的时候你发现p[i]=i即可,再注意到......
  • 七、PCL&C++相关小知识
    1、智能指针初始化(pcl库)智能指针在用的时候一定要初始化①在函数里面进行初始化pcl::PointCloud<pcl::PointXYZ>::Ptrcloud_source(newpcl::PointCloud<pcl::PointXYZ>)这里的Ptr就是智能指针,所以只看到过cloud的创建部分,而通常没有cloud的delete部分。②在类里面初始化类内部初......
  • k8s集群在扩容的是某个节点出现 Error querying BIRD: unable to connect to BIRDv4 s
    k8s集群在扩容的是某个节点出现ErrorqueryingBIRD:unabletoconnecttoBIRDv4socket1、问题背景:原因是因为新扩容的机器在扩容之前安装过docker切和当前的版本相差较大:在k8s扩容的时候扩容完发现calico异常,describe查看事件后发现报错如下:发现Kubernetes容器集群中有......
  • 2022ICPC南京站D
    1:题意给你一个序列要求你进行一次操作,选一个位置i从他开始往后加数直到加到第i+m-1个,加的值成等差求操作完后的第k大的数2:思路1):二分答案二分找到第k大的值2):差分check里面,枚举每一个数看他是否大于mid,记录为num,小于的判断他是否+等差最后一位小于mid,小于直接跳过,大于则判断......
  • 服务器重启后如何让K8S也自动重启
    在云计算和容器化时代,Kubernetes已经成为主流的容器编排解决方案,能够提供高效、自动化的服务部署和管理。然而,当服务器出现故障或需要进行维护时,我们经常需要重新启动服务器。在这种情况下,如何让Kubernetes服务也自动重启,确保其正常运行呢?以下是几个关键步骤,用于在服务器重启后自动......
  • 【7.0】基于RabbitMQ实现RPC
    【一】RPC介绍【1】介绍RPC(RemoteProcedureCall)是一种远程过程调用的协议,它允许一个计算机程序通过网络请求调用远程服务器上的一个子程序或函数。基于RabbitMQ实现的RPC可以更加可靠地实现远程过程调用。【2】分布式的系统中使用微服务之间的调用resful的接口rpc调......
  • ElasticSearch+Kibana on K8s 讲解与实战操作(版本7.17.3)
    目录一、概述二、ElasticSearch节点类型与作用三、K8s集群部署四、ElasticSearchonK8s开始部署1)下载安装包2)构建镜像3)修改yaml编排4)开始部署5)测试6)elasticsearch-head5)卸载五、Kibana编排部署1)下载安装包2)构建镜像3)修改yaml编排4)开始部署5)测试验证6)卸载六、Elasticsearch7......
  • k8s 安装
    VM U盘的安装云镜像的能力,网络初始化的内容1.VM安装直接安装:开启虚拟机的假定安装信息的,网络信息10.2021 2.Docker安装重启下对应的uname-a的版本3.重启完成下一个新的4.44再去重启一下,步骤费事,使用k8s并不会但是最好更新过来,uname-a启动docker 设置为自己的开启自己,s......
  • 【k8s】k8s构建mysql双主集群
    背景当前很多开源系统都是基于k8s,而部署时会遇到组件包含mysql的情况。理想的情况下,是将mysql迁移到云上托管。但实际情况可能比较复杂,比如这个开源框架需要mysql的DML权限,但是公司DBA不提供;而基于k8s部署mysql的问题是,这个mysql集群往往是单点的。一般mysql会依赖于一个本地挂......
  • 2019-2020 ACM-ICPC Brazil Subregional Programming Contest
    D.DenouncingMafia给定一颗树,然后给定\(k\)个起点,对于每个起点来说,从该点到根节点的一条链都会被染色,求最多有几个点会被染色\(3\leqn\leq1e5,1\leqk\leqn\)题解我们贪心的来看,起点一定会选择在叶子节点,假设叶子节点的数量为\(cnt\),所以如果\(k\geqcnt\),那么......