性能调优--gzip缓存content-download逐针渲染Queueing动态延迟加载最小化主线程工作
Posted 奋飛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能调优--gzip缓存content-download逐针渲染Queueing动态延迟加载最小化主线程工作相关的知识,希望对你有一定的参考价值。
本文主要讲述,关于 Chrome Content Download 时间过长问题调查经过,及相关优化方案
chunk-136cc8c0.js
是上图地图 geojson,587 kB 用了 1.01s,作为系统首页,会导致中间地图(视觉中心)出现长时间的空白,对于用户体验来说,极其不友好。
怀疑一:是不是网络环境不好导致?
在开发环境下运行(本地)也会出现类似情况,且更重要的是 chunk-7182b1fa.js
933kB 才用了 35ms。因此可以排除网络坏境导致。
怀疑二:缓存导致?
某些 chunk 请求用时短,某些请求用时长;是不是缓存起的作用。
- 缓存(304)的确可以加快资源的请求
- 但对于
chunk-136cc8c0.js
和其他请求依然不是一个量级的
怀疑三:浏览器并行加载个数有限,导致排队时间过长?
Chrome 浏览器默认并行加载个数为 6 个,过多的请求的确会引起资源请求排队。
通过 Chrome => Network => Timing 查看:
Queueing(排队): 浏览器会在以下情况下对请求进行队列处理:
- 有更高优先级的请求。(浏览器 Network 中可以通过 Priority 查看)
- 已经建立了 6 个 TCP 连接(HTTP/1.0和HTTP/1.1协议下的限制)
- The browser is briefly allocating space in the disk cache
Stalled(阻塞): 请求可能会因为 Queueing 中描述的任何原因而停止
Request sent(发送请求): 发送HTTP请求的时间(从第一个bit到最后一个bit)
Waiting (TTFB)(等待响应): 浏览器正在等待响应的第一个字节。TTFB表示时间到第一个字节。这个时间包括1个延迟往返和服务器准备响应所花费的时间
Content Download(下载): 下载HTTP响应的时间(包含头部和响应体)
整体 Queueing(排队)并没有消耗多少时间(1.28ms),而是 Content Download 消耗了大量时间(1.01s)。所以断定并不是排队时间过长导致该问题。
更详细的,可以查看 Chrome 开发文档:这里
怀疑四:Cpu 被耗尽,浏览器”停顿“?
查了相关的帖子,这篇 最后的回答「Frontend app is doing too much work」给了另一个方向。
是不是初始阶段进行大量的 JS 工作,导致 cpu 被耗尽了,使得浏览器处于了”停顿“状态。
通过 Chrome => Performance => Start recording:
chunk-136cc8c0.js
加载前后 cpu 使用率为 100%,导致资源请求和后续等待执行的时间都很长。初步断定,和 cpu 占用率用很大关系。
问题有了方向,后续就是针对问题出各种优化方案
优化方式
通用:开启缓存
nginx 开启静态资源(css、js)缓存配置:
在 server 中的 location / 的配置中增加如下配置
location / {
if ($request_filename ~* ^.*?\\.(js|css)$){
# 缓存时效性可以根据项目诉求,自行设定
expires 30d;
}
root xxx/dist;
index index.html;
....
}
通用:开启 Gzip
对静态资源进行 gzip,能起到巨大的效果(实测:40M的内容,gzip完不足10M)。
第1步:按照依赖
$ npm install compression-webpack-plugin --save-dev
第2步:vue.config.js
开启 gzip 压缩
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
plugins: [new CompressionPlugin()],
};
详细配置可查看 compression-webpack-plugin
第3步:在 Nginx 配置文件中的 http 或者 server 中增加配置
server {
gzip on;
gzip_vary on;
gzip_types application/javascript text/css;
location / {
...
}
}
由于是 geojson,压缩空间有限但也减少了一半,对于 http 请求速度会有很大的提高(对于纯 css、js代码,效果会更加明显)。
可选:动态延迟加载
页面中存在好多 Dialog 等下钻需要的组件,可以通过 webpack import()
动态加载,避免进入页面全部发起请求。
import()
可以动态的加载模块。调用 import
的之处,被视为分割点,被请求的模块和它引用的所有子模块,会分割到一个单独的 chunk 中。
原形式:
import detailEventDialog from './components/detail-event.vue'
import detailVulnDialog from './components/detail-vuln.vue'
import detailCompDialog from './components/detail-comp.vue'
import detailWeakDialog from './components/detail-weak.vue'
import detailWeakTabDialog from './components/detail-weak-tab.vue'
import detailAssetDialog from './components/detail-asset.vue'
import detailUserDialog from './components/detail-user.vue'
export default {
components: {
detailEventDialog,
detailVulnDialog,
detailCompDialog,
detailWeakDialog,
detailWeakTabDialog,
detailAssetDialog,
detailUserDialog
}
}
修改后:
export default {
components: {
detailEventDialog: () => import('./components/detail-event.vue'),
detailVulnDialog: () => import('./components/detail-vuln.vue'),
detailCompDialog: () => import('./components/detail-comp.vue'),
detailWeakDialog: () => import('./components/detail-weak.vue'),
detailWeakTabDialog: () => import('./components/detail-weak-tab.vue'),
detailAssetDialog: () => import('./components/detail-asset.vue'),
detailUserDialog: () => import('./components/detail-user.vue')
}
}
import()
必须至少包含一些关于模块的路径信息。打包可以限定于一个特定的目录或文件集,以便于在使用动态表达式时 - 包括可能在 import()
调用中请求的每个模块。例如, import(./locale/${language}.json)
会把 .locale
目录中的每个 .json
文件打包到新的 chunk 中。在运行时,计算完变量 language
后,就可以使用像 english.json
或 german.json
的任何文件。 – webpack import
可选:DOM逐针渲染
vue mixins
export default function (maxCount = 0) {
return {
data () {
return {
// 展示权重,默认为0
displayPriority: 0,
running: true
}
},
mounted () {
this._startLoop()
},
methods: {
_startLoop () {
// 最大渲染数为0时,停止助阵逐帧渲染
if (maxCount === 0) return
this.running = true
const step = () => {
this.displayPriority++
if (this.running && this.displayPriority <= maxCount) {
requestAnimationFrame(step)
}
}
requestAnimationFrame(step)
},
_stopLoop () {
this.running = false
},
delayRender (priority) {
return this.displayPriority >= priority
}
},
activated () {
// 激活状态开始渲染
!this.running && this._startLoop()
},
deactivated () {
// 停止渲染监听
this._stopLoop()
},
beforeDestroy () {
// 停止渲染监听
this._stopLoop()
}
}
}
使用
<template>
<div>
<el-row>...</el-row>
<el-row v-if="delayRender(3)">...</el-row>
<el-row v-if="delayRender(6)">...</el-row>
<el-row v-if="delayRender(9)">...</el-row>
</div>
</template>
<script>
export default {
mixins: [delayRenderService(9)]
}
</script>
针对业务代码瘦身
针对特定业务需求,对静态资源进行了瘦身(删除了一些无用的第三方、自研组件、及静态资源的引用);然后将 chunk-136cc8c0.js
(geojson) 加载时机提前,获取到资源后,先行渲染底图。
最小化主线程工作
浏览器的渲染器进程将代码转换为用户可以与之交互的网页。默认情况下,渲染器进程的主线程通常处理大部分代码:它解析 HTML 并构建 DOM,解析 CSS 并应用指定的样式,以及解析、评估和执行 JavaScript。主线程还处理用户事件。因此,每当主线程忙于做其他事情时,网页可能无法响应用户交互,从而导致糟糕的体验。
方式 | |
---|---|
Script evaluation | 优化第三方 JavaScript 去抖动输入处理程序 使用网络工作者 |
Style and layout | 减少样式计算的范围和复杂性 避免大型、复杂的布局和布局颠簸 |
Rendering | 坚持只使用合成器的属性并管理层数 简化油漆复杂性并减少油漆区域 |
Parsing HTML and CSS | 提取关键 CSS 缩小 CSS 推迟非关键 CSS |
Script parsing and compilation | 通过代码拆分减少 JavaScript 负载 删除未使用的代码 |
Garbage collection | 使用以下命令监控网页的总内存使用情况 measureMemory() |
最终效果
以上是关于性能调优--gzip缓存content-download逐针渲染Queueing动态延迟加载最小化主线程工作的主要内容,如果未能解决你的问题,请参考以下文章
性能调优--gzip缓存content-download逐针渲染Queueing动态延迟加载最小化主线程工作
这都2021年了,还不会Feign性能调优?Feign性能调优之gzip压缩实现-自娱自乐篇
使用springboot cache + redis缓存时使用gzip压缩以提升性能