Karam
Articles71
Tags52
Categories15
招新项目第二次维护复盘

招新项目第二次维护复盘

项目遗留问题

  1. 之前为了不卡顿,删除了鼠标跟随动画
  2. 项目的首屏加载时间有待优化
  3. 没有考虑在不同的网速下如何优化
  4. 首次加载幻灯片图片时会有空白时间
  5. 箭头动画效果比较迟钝
  6. 内容溢出
  7. 项目没有做埋点,无法监控页面情况

优化鼠标跟随动画

使用computed属性缓存跟随元素的style属性
原本的跟随动画卡顿的原因是,过多的刷新dom导致卡顿
而computed属性会基于他们的依赖缓存,当依赖发生改变的时候,触发dom更新

项目的首屏加载时间

首先,在正常网络速度的情况下,我得到的首屏加载情况是:

得到加载并解析完HTML文件后,得到DCL时间1045.4 毫秒
开始渲染直出的HTML文件,得到FP时间1192.1 毫秒
然后触发FCP,时间为1192.1 毫秒
把所有静态资源文件下载完毕后触发L,时间为2527.6 毫秒
这里先从加载HTML文件开始优化,首次加载HTML文档内容应该在14kb以内,这里我的HTML文档只有1.1kb,符合要求
第二步删除内联JS,css,没有,跳过步骤
第三步减少Javascript包大小,减少css文件大小,减少图片文件大小
使用gzip压缩,生成一个gz文件,上线服务器后访问的就是压缩后的文件

资源加载

上面的图片,我在加载首页的轮播图之前,加载了首页显示无必要的图片,所以这里对图片进行懒加载。
使用vue-lazyload后,减少了图片加载的事件,效果为:

DCL时间缩短了200ms左右
当前我的首页各项性能指标为:
FP:613ms
FCP:613ms
LCP:713ms
Load:1492ms

缩小CSS
使用webpack打包工具打包代码,这里使用了mini-css-extra-plugin来将css代码从文件中分离出来,webpack-fix-style-only-entries
效果为:
FP:392ms
FCP:392ms
LCP:659ms
Load:1268ms
查看代码的覆盖率:

很多代码的覆盖率甚至小于50%,查询后无用代码,无用代码基本上是我引入的animate.css,element-ui的css,大部分大部分代码都没用上
一般而言可以剔除js的无用代码,那想要剔除无用的css代码,可以使用purgess-webpack-plugin,使用UselessFile删除无用文件,最后根据FP时间点之前的时间执行,查看长任务,延迟执行非关键代码,剔除无用文件,最后生成的文件css大小由272kb到87kb,js文件大小由1.98M到1.28M
最后首页各项性能指标为:
FP:339ms
FCP:339ms
LCP:795ms

项目埋点

做了个简单的项目各项指标的计算,但是并没有监控,主要是和后端没有协商,我自己另外加的功能,故用于自己观察页面性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
const ttiPolyfill = require('tti-polyfill')
const timing = performance.timing

let Redirect //重定向耗时
let Cache //缓存耗时
let DNS //DNS域名解析耗时
let TCP //TCP连接耗时
let SSL //SSL安全连接耗时
let Request //从SSL连接建立好后,从客户端发送至服务端首次响应的耗时
let Response //从服务端首次响应至数据完全响应完成的耗时
let FP //首次绘制元素耗时
let FCP //首次有内容渲染的时间
let LCP //最大内容绘制时间
let TTI //可交互时间
let FMP //首次有意义内容渲染的时间
let FID //记录用户与页面进行首次交互操作所花费的时间
let MPFID //在页面加载过程中用户和页面进行首次交互操作可能花费的最长时间
let DOMReady //执行完所有同步脚本的耗时
let Load //从URL输入请求到事件完全加载的耗时
let DOMParse //从请求响应结束至Dom可交互的耗时
let Processing //Dom解析开始至结束的耗时

//最佳性能的指标
const bestPerformance = {
FCP: {
min: 1182,
max: 2668
},
FP: {
min: 1000,
max: 2500
},
LCP: {
min: 2500,
max: 4000
},
TTI: {
min: 1500,
max: 5000
}
}

//获取指标数据
export function getIndex() {
if (!performance) {
console.info('浏览器暂不支持Performance API')
} else {
Redirect = timing.redirectEnd - timing.redirectStart

Cache = timing.domainLookupStart - timing.fetchStart

DNS = timing.domainLookupEnd - timing.domainLookupStart

TCP = timing.connectEnd - timing.connectStart

if (timing.secureConnectionStart) SSL = timing.connectEnd - timing.secureConnectionStart
else SSL = '未使用HTTPS'

Request = timing.responseEnd - timing.requestStart

Response = timing.responseEnd - timing.responseStart

if (typeof performance.getEntries === "function") {
performance.getEntries().forEach((entry) => {
if(entry.name === 'first-paint')
FP = entry.startTime
else if(entry.name === 'first-contentful-paint')
FCP = entry.startTime
})
} else {
if(chrome && chrome.loadTimes) {
let laodTimes = window.chrome.loadTimes()
let { firstPaintTime, startLoadTime } = loadTimes
FP = firstPaintTime - startLoadTime
} else if(performance.timing && typeof performance.timing.msFristPaint === "number") {
let { msFirstPaint, navigationStart } = performance.timing
FP = msFirstPaint - navigationStart
}
}

LCP = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (entry.startTime >= bestPerformance.LCP.max) {
console.warn('最大内容绘制时间过长')
console.info(`优化建议:`)
}
}
}).observe({
type: 'largest-contentful-paint',
buffered: true
})

ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
TTI = tti
console.log('TTI时间为' + tti + 'ms')
if(TTI > bestPerformance.TTI.max) {
console.error('可交互时间过长')
console.info(`优化建议:`)
}
})

getCLS()

DOMReady = timing.domContentLoadedEventEnd - timing.fetchStart

Load = timing.loadEventEnd - timing.navigationStart

DOMParse = timing.domInteractive - timing.responseEnd

Processing = timing.domComplete - timing.domLoading
//console.log(timing)
}
}

function getCLS() {
let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Only count layout shifts without recent user input.
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// If the entry occurred less than 1 second after the previous entry and
// less than 5 seconds after the first entry in the session, include the
// entry in the current session. Otherwise, start a new session.
if (sessionValue
&& entry.startTime - lastSessionEntry.startTime < 1000
&& entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
sessionValue = entry.value;
sessionEntries = [entry];
}
// If the current session value is larger than the current CLS value
// update CLS and the entries contributing to it.
if (sessionValue > clsValue) {
clsValue = sessionValue;
clsEntries = sessionEntries;
// Log the updated value (and its entries) to the console.
//console.log('CLS:', clsValue, clsEntries)
if(clsValue >= 0.25) {
console.warn('布局偏移分数比较大')
console.info(`优化建议:
`)
}
}
}
}}).observe({type: 'layout-shift', buffered: true});
}

首次加载的空白时间

原因是图片的加载时间过长,平均在500ms左右,性能不理想,可以考虑压缩图片,切换图片格式

内容溢出

部分内容在元素框外部,使用鼠标拖动会移动到元素框外面,设置user-select: none,用户不可以选中元素

弱网下优化

我个人认为,弱网条件下,主要考虑用户的体验,可以设置loading,骨架屏,关键资源优先加载,图片切换低清晰度,尽快让用户看到内容