Web优化躬行记(6)——优化闭环实践

简介: Web优化躬行记(6)——优化闭环实践

 在遇到一个页面性能问题时,我理解的优化闭环是:分析、策略、验证和沉淀。

  • 分析需要有分析数据,因此得有一个性能监控管理。
  • 策略就是制订针对性的优化方案,解决当前遇到的问题。
  • 验证的对象上述策略,判断方案是否有效,同样需要数据支撑。
  • 沉淀就是将解决过程文档化、通用化,能够总结成一套实际方案、优化规则等。

  这其中非常关键的一步是需要采集到性能数据,并且得有个可视化后台查看数据变化。

  在之前已经自制了一个性能优化平台,采集前端性能参数的 SDK 叫 shin.js


一、优化的三部分


  在文章开头,我想先聊聊网页优化的三部分:网络,渲染和容器。

  第一部分的网络就是提升传输速度,可优化的手段包括 gzipped压缩、CDN、HTTP 缓存、HTTP 2.0协议、并发请求等。

  像 HTTP 缓存分为强缓存和协商缓存,请求首部和浏览器配合完成资源的缓存机制,下图摘自《前端程序员面试笔试宝典》。

  

  第二部分的渲染就是 CRP 优化(关键渲染路径),CRP 是指浏览器从接收资源到渲染像素的过程。

  优化的点包括资源数、字节数和加载时序。现代化的 webpack 构建工具就会对资源做前两项的优化处理,包括压缩文件、合并文件、优化包的引入等。

  加载时序就包括日常都会用的图片懒加载和预加载、脚本的延迟(defer)、异步(async)和预加载(preload)等。

  对于比较庞大的首页,可以先将那些能阻塞网页首次渲染的关键资源载入,其余资源都延迟载入,以此提升页面打开速度。

  第三部分的容器(WebView)就是借用端的能力,让 APP 配合优化网页。

  例如预请求,将请求接口的时机前置到容器打开之时,下面是一张实现流程图。

  

  还有一种静态资源缓存至客户端本地,当时与公司客户端讨论此方案时,他们觉得每次拦截请求会损伤性能,后面就采用了折中的办法。

  就是他们去主动请求特定地址的静态资源,然后开放接口让我可以去读取本地资源,也就是说由 Web 来控制是否读取缓存资源。


二、问题引出  


  现在言归正传,回到本次的优化中来。

  为了提升页面产出率,联合 UI 设计构建了一套可配置的通用活动模板

  活动上线后,就查看了性能数据,情况很不理想,如下图所示。

  

  FP(白屏)时间大部分都在 2 秒以上,取平均值更是在 3 秒左右。Google的报告指出:

  • 如果网页加载时间从 1 秒增加到 3 秒,跳出率就会提高 32%
  • 如果网页加载时间从 1 秒增加到 6 秒,跳出率就会上升 106%

三、数据排查


  在数据库中,将指定的性能数据记录导出到 Excel 中。

  翻了一条后发现,性能问题集中在 DOM 中。

{
    "unloadEventTime": 0,
    "loadEventTime": 1,
    "interactiveTime": 1255,
    "parseDomTime": 1075,
    "initDomTreeTime": 721,
    "readyStart": 5,
    "redirectCount": 0,
    "compression": 0,
    "redirectTime": 0,
    "appcacheTime": 0,
    "lookupDomainTime": 0,
    "connectSslTime": 0,
    "connectTime": 0,
    "requestTime": 119,
    "requestDocumentTime": 119,
    "responseDocumentTime": 0,
    "TTFB": 534,
}

  JSON 中的 interactiveTime、parseDomTime 和 initDomTreeTime 消耗的时间都不短,计算规则如下所示。

/**
     * 解析 DOM 树结构的时间
     * 期间要加载内嵌资源
     * 反省下你的 DOM 树嵌套是不是太多了
     */
    api.parseDomTime = timing.domComplete - timing.domInteractive;
    /**
     * 请求完毕至DOM加载耗时
     */
    api.initDomTreeTime = timing.domInteractive - timing.responseEnd;
    /**
     * 首次可交互时间
     */
    api.interactiveTime = timing.domInteractive - timing.fetchStart;

  参考 W3C 第二版性能参数图可知,慢的地方集中在 Processing 阶段。

  


四、Chrome DevTools


  打开 Chrome DevTools 中的 Performance 一栏,录制后,可在火焰图中看到长任务。

  点击 Long task 链接,会跳转到使用 RAIL 模型衡量性能一文。

  

  在 PC 浏览器中打开肯定会比在手机中快,但即使如此,还是出现了性能瓶颈,说明这里是真的慢。

  蓝底的 DCL 是 DOMContentLoaded 事件,在 HTML 文档被完全加载和解析后触发,绿底的 FP 就是白屏时间。

  黄底的 Evaluate Script 表示加载 JavaScript 脚本,Compile Script 表示执行 JavaScript 脚本。

  再来看看网络请求瀑布图,下图中的蓝线就是 DCL,可以清晰的看到,蓝线之前在加载的基本都是 JavaScript 脚本。

  

  由此可知,加载的脚本有点多,并且有一个 chunk-vendors.js 脚本还比较大,下载时间有点长(依据蓝色块)。


五、代码分析


  定位到了问题根源,那就直接查看基于 Vue 的代码是怎么写的了。

1)HTML

  下面是编译后的页面 HTML 结构,只列出了关键部分。

<!DOCTYPE html>
<html lang=en>
<head>
  <script src=https://res.wx.qq.com/open/js/jweixin-1.6.0.js></script>
  <script src=//www.xxxx.com/flexible/flexible.js></script>
  <script src=//www.xxxx.co/files/js/baidu.js></script>
  <script src=//www.xxxx.co/files/js/shin.js></script>
  <link href=//www.xxxx.me/game/css/operation37.cba04f10.css rel=preload as=style>
  <link href=//www.xxxx.me/game/js/chunk-lodash.152ef24b.js rel=preload as=script>
  <link href=//www.xxxx.me/game/js/chunk-lottie.23b9982e.js rel=preload as=script>
  <link href=//www.xxxx.me/game/js/operation37.fa5f5378.js rel=preload as=script>
  <link href=//www.xxxx.me/game/css/chunk-vendors.779f7d1d.css rel=stylesheet>
  <link href=//www.xxxx.me/game/css/operation37.cba04f10.css rel=stylesheet>
</head>
<body>
  <div id=app></div>
  <script src=//www.xxxx.me/game/js/chunk-vendors.ca022e99.js></script>
  <script src=//www.xxxx.me/game/js/operation37.fa5f5378.js></script>
</body>
</html>

  首先在 head 中,引入了大量的 JavaScript 脚本,flexible.js 其实在构建时可以内联,不需要网络访问。

  然后 jweixin-1.6.0.js 和 baidu.js 这两个脚本完全可以延迟加载,后者就是增加百度统计的脚本。

  接着就是 shin.js 需要做压缩处理,可以减少 50% 以上的尺寸。

  在 link 元素中,使用了 preload,表示可并行的预加载,并且不会执行,这是提升页面性能的一种手段。

  虽然第三方的库(chunk-vendors.ca022e99.js)和业务主逻辑(operation37.fa5f5378.js)两个脚本声明在 body 中。

  但是主结构就是个空的 div,因此在加载和运行时就会延长 DOM 的解析,影响白屏时间。

2)vendors 优化

  Vue 内置了一条命令,可以查看每个脚本的尺寸以及内部依赖包的尺寸。

  在下图中,vendors.js 的原始尺寸是 3.76M,gzipped 压缩后的尺寸是 442.02KB,比较大的包是 lottie、swiper、moment、lodash 等。

  

  这类比较大的包可以再单独剥离,不用全部聚合在 vendors.js 中。

  在 vue.config.js 中,配置 config.optimization.splitChunks(),如下所示,参数含义可参考官网

config.optimization.splitChunks(
      {
        cacheGroups: {
          vendors: {
            name: 'chunk-vendors',
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            chunks: 'initial'
          },
          lottie: {
            name: 'chunk-lottie',
            test: /[\\/]node_modules[\\/]lottie-web[\\/]/,
            chunks: 'all',
            priority: 3,
            reuseExistingChunk: true,
            enforce: true
          },
          swiper: {
            name: 'chunk-swiper',
            test: /[\\/]node_modules[\\/]_swiper@3.4.2@swiper[\\/]/,
            chunks: 'all',
            priority: 3,
            reuseExistingChunk: true,
            enforce: true
          },
          lodash: {
            name: 'chunk-lodash',
            test: /[\\/]node_modules[\\/]lodash[\\/]/,
            chunks: 'all',
            priority: 3,
            reuseExistingChunk: true,
            enforce: true
          }
        }
      }
    )

  在经过一顿初步操作后,原始尺寸降到 2.4M,gzipped 压缩后的尺寸是 308.64KB,比之前少了 100 多 KB。

  

  现在在入口处需要单独声明依赖的包,否则不会自动引入。

pages: {
   operation37: {
      entry: 'src/pages/operation37/index.js',
      template: 'src/pages/operation37/index.html',
      filename: 'operation37.html',
      title: '榜单配置页面',
      chunks: ['chunk-lottie', 'operation37', 'chunk-vendors']
   },
}

  其实大部分的 H5 页面都比较简单,可能就使用了包的一个小功能,那完全可以自己用代码实现,这样就不必依赖那个大包了。

  后面就是在代码逻辑层面的优化,核心就是减少脚本尺寸。优化后,再去观察数据的变化。

3)CDN加速

  之前部分静态资源采取了 CDN 加速,现在需要将 game 下面中的静态资源全部走 CDN。

  在云端配置些参数,就能走 CDN。不过,第一次没有配置好,没有配置转发路径,造成了严重的线上问题。

  第二次就比较谨慎,在测试环境将之前碰到的问题都验证后,才最终在线上配置。

  白屏时间占比变化:

  • 1 秒内的占比从 77.3% 最高提升至 78.7%
  • 1 - 2 秒占比从 15.6% 最高提升至 18.7%
  • 2 - 3 秒占比从 4% 最低下降至 1.8%
  • 3 - 4 秒占比从 1.1% 最低下降至 0.6%
  • 4 秒以上的占比从 2.1% 最低下降至 1.4%

 

参考资料:

长的 JavaScript 任务是否会延迟您的交互时间?

狙杀页面卡顿 —— Performance 工具指北

chrome performance看浏览器渲染过程

深入理解浏览器解析渲染 HTML

Vue CLI 项目页面打开时间优化:从16秒到2秒内

preload 让加载和解析解耦

分类: Web优化躬行记

相关文章
|
6月前
|
前端开发 算法 API
构建高性能图像处理Web应用:Next.js与TailwindCSS实践
本文分享了构建在线图像黑白转换工具的技术实践,涵盖技术栈选择、架构设计与性能优化。项目采用Next.js提供优秀的SSR性能和SEO支持,TailwindCSS加速UI开发,WebAssembly实现高性能图像处理算法。通过渐进式处理、WebWorker隔离及内存管理等策略,解决大图像处理性能瓶颈,并确保跨浏览器兼容性和移动设备优化。实际应用案例展示了其即时处理、高质量输出和客户端隐私保护等特点。未来计划引入WebGPU加速、AI增强等功能,进一步提升用户体验。此技术栈为Web图像处理应用提供了高效可行的解决方案。
|
5月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。
|
5月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:使用 Gin 快速构建 Web 服务
Gin 是一个高效、轻量级的 Go 语言 Web 框架,支持中间件机制,非常适合开发 RESTful API。本文从安装到进阶技巧全面解析 Gin 的使用:快速入门示例(Hello Gin)、定义 RESTful 用户服务(增删改查接口实现),以及推荐实践如参数校验、中间件和路由分组等。通过对比标准库 `net/http`,Gin 提供更简洁灵活的开发体验。此外,还推荐了 GORM、Viper、Zap 等配合使用的工具库,助力高效开发。
|
5月前
|
缓存 前端开发 应用服务中间件
Web端实时通信技术SSE在携程机票业务中的实践应用
本文介绍了携程机票前端基于Server-Sent Events(SSE)实现服务端推送的企业级全链路通用技术解决方案。文章深入探讨了 SSE 技术在应用过程中包括方案对比、技术选型、链路层优化以及实际效果等多维度的技术细节,为类似使用场景提供普适性参考和借鉴。该方案设计目标是实现通用性,适用于各种网络架构和业务场景。
174 1
|
6月前
|
缓存 前端开发 应用服务中间件
Web端实时通信技术SSE在携程机票业务中的实践应用
本文介绍了携程机票前端基于Server-Sent Events(SSE)实现服务端推送的企业级全链路通用技术解决方案。文章深入探讨了 SSE 技术在应用过程中包括方案对比、技术选型、链路层优化以及实际效果等多维度的技术细节,为类似使用场景提供普适性参考和借鉴。
225 7
|
6月前
|
Web App开发 前端开发 JavaScript
鸿蒙5开发宝藏案例分享---Web适配一多开发实践
这是一份实用的鸿蒙Web多设备适配开发指南,针对开发者在不同屏幕尺寸下的布局难题提供了解决方案。文章通过三大法宝(相对单位、媒体查询和窗口监听)详细介绍如何实现智能适配,并提供了多个实战案例,如宫格布局、对话框变形和自适应轮播图等。此外,还分享了调试技巧及工具推荐,帮助开发者快速上手并优化性能。最后鼓励读者实践探索,并提示更多官方资源等待发现。
|
8月前
|
存储 消息中间件 缓存
支持百万人超大群聊的Web端IM架构设计与实践
本文将回顾实现一个支持百万人超大群聊的Web端IM架构时遇到的技术挑战和解决思路,内容包括:通信方案选型、消息存储、消息有序性、消息可靠性、未读数统计。希望能带给你启发。
295 0
支持百万人超大群聊的Web端IM架构设计与实践
|
11月前
|
弹性计算 Java 关系型数据库
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
249 2
Web应用上云经典架构实践教学
|
10月前
|
Kubernetes Java 持续交付
小团队 CI/CD 实践:无需运维,Java Web应用的自动化部署
本文介绍如何使用GitHub Actions和阿里云Kubernetes(ACK)实现Java Web应用的自动化部署。通过CI/CD流程,开发人员无需手动处理复杂的运维任务,从而提高效率并减少错误。文中详细讲解了Docker与Kubernetes的概念,并演示了从创建Kubernetes集群、配置容器镜像服务到设置GitHub仓库Secrets及编写GitHub Actions工作流的具体步骤。最终实现了代码提交后自动构建、推送镜像并部署到Kubernetes集群的功能。整个过程不仅简化了部署流程,还确保了应用在不同环境中的稳定运行。
577 9
|
前端开发 JavaScript 开发工具
从框架到现代Web开发实践
从框架到现代Web开发实践
185 1

热门文章

最新文章

下一篇
oss云网关配置