随着需求的不断开发,前端项目不断膨胀,业务提出:你们的首页加载也太慢啦,我都需要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-lodashbabel 插件, 在 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秒左右,达到了我的预期。

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

总结

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

PC首页资源加载速度由8s降到2s的优化实践的更多相关文章

  1. 【Vuejs】317- 提升90%加载速度——Vuecli下的首屏性能优化

    点击上方"前端自习课"关注,学习起来~,所以接下来还会介绍一些它们在优化上的异同 的话,先安装插件 cnpm intall webpack-bundle-analyzer –sav ...

  2. 【Vuejs】269- 提升90%加载速度——vuecli下的首屏性能优化

    前言 之前用 ,所以接下来还会介绍一些它们在优化上的异同 分析 vuecli 2.x自带了分析工具只要运行 npm run build --report 如果是 vuecli 3的话,先安装插件 cn ...

  3. 我是如何将页面加载时间从6S降到2S的?

    写在前面 生活在信息爆炸的今天,我们每天不得不面对和过滤海量的信息--无疑是焦躁和浮动的,这就意味着用户对你站点投入的时间可能是及其吝啬的(当然有一些刚需站点除外). 如何给用户提供迅速的响应就显得十 ...

  4. vue-cli3项目首页加载速度优化(cdn加速,路由懒加载,gzip压缩)

    今天打算上线vue的单页面项目,上线后,首页加载速度巨慢! 原因是项目上线后,网速不够快,加载js,css等资源很慢, 打开打包好的文件发现chunk-vendors.xxxxxxx.js的包很大,达 ...

  5. Web前端性能优化总结——如何提高网页加载速度

    一.提高网页加载速度的必要性 国际知名的一组来自Jupiter Research的数据显示:购物者在访问网站过程中的不满会导致销售损失和品牌受损,其中 77%的人将不再访问网站 ,62%的人不再从该网 ...

  6. nginx缓存静态资源,只需几个配置提升10倍页面加载速度

    nginx缓存静态资源,只需几个配置提升10倍页面加载速度 首先我们看图说话 这是在没有缓存的情况下,这个页面发送了很多静态资源的请求:   1.png 可以看到,静态资源占用了整个页面加载用时的90 ...

  7. 优化JavaScripe 提升首页加载速度的几种方案解析

    优化目的: 1. 减少load量. 2. 优化js,加快页面加载速度. 网站中最影响网站打开速度的是什么?我会告诉是网站中的javascript,简称JS.模板中引用的JS文件越多,打开速度越慢,细读 ...

  8. 转: web 页面加载速度优化实战-100% 的飞跃提升

    前言 一个网站的加载速度有多重要? 反正我相信之前来 博主网站 的人至少有 50% 在加载完成前关闭了本站. 为啥捏? 看图 首页完整加载时间 8.18s,看来能进来看博主网站的人都是真爱呀,哈哈. ...

  9. web页面加载速度缓慢,如何优化?

    参考博客: https://www.cnblogs.com/xp796/p/5236945.html https://www.cnblogs.com/MarcoHan/p/5295398.html - ...

  10. Web前段优化,提高加载速度 css

    前言:  在同样的网络环境下,两个同样能满足你的需求的网站,一个"Duang"的一下就加载出来了,一个纠结了半天才出来,你会选择哪个?研究表明:用户最满意的打开网页时间是2-5秒, ...

随机推荐

  1. CSharp初体验

    入门 初来乍到了解一门新的语言,它可能和熟悉的c/c++有不小差别,整体上需要首先了解下语法文件的整体结构.例如,源文件整体结构如何. 乍看CSharp源文件(compile unit)的结构,官网主 ...

  2. HashMap 底层是如何实现的?

    HashMap 在不同的 JDK 版本下的实现是不同的,在 JDK 1.7 时,HashMap 底层是通过数组 + 链表实现的:而在 JDK 1.8 时,HashMap 底层是通过数组 + 链表或红黑 ...

  3. Spring 核心概念之一 IoC

    前言 欢迎来到本篇文章!通过上一篇什么是 Spring?为什么学它?的学习,我们知道了 Spring 的基本概念,知道什么是 Spring,以及为什么学习 Spring.今天,这篇就来说说 Sprin ...

  4. python 星号(*) 还能这么用

    哈喽大家好,我是咸鱼 今天跟大家介绍一下 python 当中星号(*)的一些用法 首先大家最常见的就是在 python 中 * 是乘法运算符,实现乘法 sum = 5 * 5 # 25 除此之外,还有 ...

  5. 如何构建您的第一部AWS数据库服务

    目录 2.1. 基本概念解释 2.2. 技术原理介绍 2.3. 相关技术比较 实现步骤与流程 2.3.1 准备工作:环境配置与依赖安装 2.3.2 核心模块实现 2.3.3 集成与测试 4. 应用示例 ...

  6. Java使用joml计算机图形学库,将3D坐标旋转正交投影转为2D坐标

    最近遇到了一个困扰我许久的难题,现将解决方案分享出来 由于我们的项目侧重点在前端绘图,导致了前后端工作量不协调,我后端接口很快就能写完,而前端一个图要画好久,领导见状将前端的任务分到后端一部分用Jav ...

  7. Python 一大坑,配置文件中字典引用问题(拷贝)。

    大坑 +1 python 配置文件中字典引用问题 最近在开发系统时发现一个传奇的BUG, 用户未登录就可进入系统内,而且含有真实身份信息. 此问题困扰多时,反复debug.由于找不到问题原因,复现具有 ...

  8. 2022蓝桥杯B组(java)版

    2022蓝桥杯b组 A题 import java.math.BigInteger; public class A { public static void main(String[] args) { ...

  9. 即构 SDK 6月迭代:新增拉流画面镜像等功能,为开发者提供更大便利

    即构SDK6月新版本已上线,本月SDK迭代主要新增了拉流画面镜像功能,媒体播放器新增支持缓存相关的设置,新增支持设置对焦模式和曝光模式等功能,多个功能模块的灵活设置,让开发者能更便利的自定义选择,为用 ...

  10. 从TL、ITL到TT

    1.概述 ThreadLocal(TL)是Java中一种线程局部变量实现机制,他为每个线程提供一个单独的变量副本,保证多线程场景下,变量的线程安全.经常用于代替参数的显式传递. Inheritable ...