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

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

性能指标

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

结果指标

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

1_webVitals.png

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

2_fcp、lcp.png

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

3_优化前fcp.png

过程指标

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

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

4_优化前过程指标.png

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

页面生命周期

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

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

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

通用优化

删除无用代码

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

无用 cdn 资源

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

5_删除无用cdn.png

功能重复的资源

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

6_重复cdn.png
无用依赖

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

7_无用npm.png

无用请求逻辑

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

无用文件

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

网络相关

域名收敛

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

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

8_dns查找.jpeg

通过域名收敛,将相同的应用/资源整合到同一域名,减少不同域名之间 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 中 <link> 元素通过 dns- prefetch 的 rel 属性提供这项功能,然后在 href 属性中指定要跨域的域名(仅作用于与当前页面不一样的域名)。

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

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

编译产物优化

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

9_编译产物.png

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

合并 echarts 版本

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

10_去除echarts4.21.png

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

11_echarts自身.png
12_业务组件.png

优化方案是找一个各组件都可以使用版本,将 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。

13_编译优化对比.png

devtool

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

lighthouse 建议

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

14_lighthouse建议.png

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

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

方案大概是这样:

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

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

network 查看接口响应

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

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

优化结果

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

15_指标对比.png

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

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

总结

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

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

推荐阅读更多精彩内容

  • 前言 因为这系统之前一直在不断迭代,现在暂时告一段落了,我赶紧趁着开年工作不多的空余时间,把这个系统优化一下,不然...
    0月阅读 781评论 0 1
  • 在互联网发展的当下,webApp 项目越来越大,需求越来越繁重,功能越来越大,导致代码打包体积越来越大,页面的打开...
    幸福拾荒者阅读 584评论 0 0
  • 1. 用户体验 软件是用户通往资源的通道。例如:滴滴对应司机、乘客;淘宝、天猫对应购物;饿了么、美团对应周边生活;...
    nimw阅读 486评论 0 1
  • 性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 《前端性能优化(中)...
    9吧和9说9话阅读 471评论 0 1
  • >> 原文链接[https://nojsja.gitee.io/blogs/2021/05/04/48105820...
    nojsja阅读 1,071评论 0 1