
1
首屏加載性能 首屏加載耗時(shí),TP75從原來的2.47秒減小到了1.58秒,性能提升約36.03%。圖2所示為優(yōu)化前后以周為維度的首屏TP75加載時(shí)長(zhǎng)。
2
服務(wù)器性能 通過對(duì)服務(wù)器進(jìn)行壓測(cè),在相同QPS維度下,CPU從原來的29.52%降低至20.5%,CPU使用率相比之前降低了30.5%
1
Performance API
Performance API是ECMAScript5才引入的,精度可達(dá)到1毫秒的千分之一,目前主流瀏覽器基本都已經(jīng)支持performance對(duì)象。通過performance.timing對(duì)象,可以拿到瀏覽器處理網(wǎng)頁各個(gè)階段的耗時(shí)。通過performance.getEntries方法,可以獲取js, css, 圖片及ajax在內(nèi)的所有請(qǐng)求的耗時(shí)信息。 我們基于Performance API,封裝了一個(gè)前端測(cè)速模塊,該模塊在頁面加載完成后將所需性能數(shù)據(jù)上報(bào)至服務(wù)器,之后可以在可視化平臺(tái)上進(jìn)行數(shù)據(jù)的展示及分析。2
Chrome Devtools
Chrome Devtools是前端調(diào)試及性能分析常用的工具,通過該工具,可以查看頁面資源加載情況,所加載CSS、JS及圖片的大小,還可以通過Performance面板,查看頁面渲染繪制和Script執(zhí)行情況。3
v8-profiler
通天塔H5是基于React SSR架構(gòu)的,頁面首屏在Node中間層請(qǐng)求數(shù)據(jù)并渲染,所以除純前端的監(jiān)控及分析,還需對(duì)Node層進(jìn)行性能分析及優(yōu)化。在Node層性能分析中,我們主要通過v8-profiler模塊進(jìn)行性能分析。 在本地或測(cè)試環(huán)境下新增兩個(gè)路由:const profiler = require('v8-profiler');
router.get('/profiler/start', (req, res) => {
//Start Profiling
profiler.startProfiling('CPU profile');
res.end('profile start');
});
router.get('profiler/end', (req, res) => {
const profile = profiler.stopProfiling()
profile.export()
.pipe(res)
.on('finish', () => {
profile.delete();
res.end();
});
});
通過ab壓測(cè)工具,對(duì)服務(wù)器發(fā)起請(qǐng)求
ab -c 10 -n 1000 http://localhost:7001/mall/active/xxx/index.html
在壓測(cè)過程中,可以通過 http://localhost:7001/profiler/start開始性能統(tǒng)計(jì),通過http://localhost:7001/profiler/end結(jié)束性能統(tǒng)計(jì),并將結(jié)果保存為 ***.cpuprofile文件,通過Chrome Devtools中的JavaScript Profiler工具進(jìn)行分析。
1
前端常規(guī)優(yōu)化 在優(yōu)化之初,根據(jù)《高性能網(wǎng)站建設(shè)指南》提及的常規(guī)優(yōu)化方案,我們檢查了項(xiàng)目中需要改進(jìn)或深度優(yōu)化的地方,主要涉及以下方面:- 盡可能的減少HTTP的請(qǐng)求數(shù),減小HTTP請(qǐng)求大小
- 將靜態(tài)資源放在CDN,最大化利用CDN緩存能力
- 減少CSS和JS請(qǐng)求個(gè)數(shù),減小CSS和JS包大小
- 啟用gzip壓縮

const isJfsRegex = /360buyimg\.com\/.*\/((s([\d^_]+)x([\d^_]+)_)?jfs)/i;
export function resizeImg(url, rect) {
const dpr = window.devicePixelRatio;
if (!isJpegRegExp.test(url)) {
return url;
}
const result = url.match(isJfsRegex);
if (!result) {
return url;
}
if (result[3] && result[4]) {
if (!rect || (!rect.width && !rect.height)) {
return url;
}
if (rect.width && !rect.height) {
rect.height = rect.width / result[3] * result[4];
}
if (rect.height && !rect.width) {
rect.width = rect.height / result[4] * result[3];
}
const t = 's' + Math.ceil(rect.width* dpr) + 'x' + Math.ceil(rect.height* dpr) + '_jfs';
return url.replace(result[1], t);
} else {
if (rect && rect.width && rect.height) {
return url.replace('/jfs/', `/s${Math.ceil(rect.width * dpr)}x${Math.ceil(rect.height * dpr)}_jfs/`);
}
}
return url;
}
2)最大化利用CDN緩存
在做性能優(yōu)化前,通天塔靜態(tài)資源的打包,是開發(fā)者在上線前,在自己電腦上進(jìn)行的,且文件名會(huì)依據(jù)文件內(nèi)容重新生成,格式為[filename].[contenthash:8].js。
按這種方式在個(gè)人電腦上打包,即使有package-lock.json鎖定包版本,但由于個(gè)人電腦操作系統(tǒng)及使用的npm包管理工具的不同(有的包管理工具不讀package-lock.json),node_module下的文件可能會(huì)不一致,導(dǎo)致文件的contenthash不同。
針對(duì)這個(gè)問題,我們基于Jenkins搭建了一個(gè)前端CI打包系統(tǒng),后繼所有上線前的前端靜態(tài)資源打包,都遷移到CI上進(jìn)行,通過這種方式,確保了文件名的一致性,以最大程度的利用CDN緩存。
3)調(diào)整webpack打包策略,按需加載CSS和JS
在性能優(yōu)化前,通天塔的CSS和JS資源是按以下策略打包的:
- vendor.[contenthash:8].js: 包含node_module下的代碼
- common.[contenthash:8].js: 包含非node_modules下的代碼
- [channel].[contenthash:8].js: 通天塔有很多渠道,每個(gè)渠道的專屬代碼打包到這個(gè)JS中
- template.[contenthash:8].css: 包含所有渠道通用CSS
- [channel].[contenthash:8].css: 包含渠道專有CSS
- vendor.[contenthash:8].js: 包含node_module下的JS文件
- lowUsedTemp.[contenthash:8].js: 包含使用頻率低的系統(tǒng)模板代碼,頁面會(huì)按照活動(dòng)是否使用到低頻模板按需請(qǐng)求
- mute.[contenthash:8].js: 包含剔除低頻使用模板后,較為穩(wěn)定,很少改動(dòng)的系統(tǒng)模板
- template.[contenthash:8].js: 包含剩余非node_modules下的代碼
- [channel].[contenthash:8].js: 包含渠道專屬代碼
- lowUsedTemp.[contenthash:8].css: 包含使用頻率低的系統(tǒng)模板代碼的CSS,頁面會(huì)按照活動(dòng)是否使用到低頻模板按需請(qǐng)求
- template.[contenthash:8].css: 包含所有渠道通用CSS
- [channel].[contenthash:8].css: 包含渠道專有CSS
2
業(yè)務(wù)優(yōu)化 在常規(guī)的前端性能優(yōu)化達(dá)到瓶頸后,我們開始嘗試基于業(yè)務(wù)進(jìn)行性能優(yōu)化。 1)首屏精準(zhǔn)化優(yōu)化 通天塔頁面是運(yùn)營(yíng)在可視化配置平臺(tái)中,通過選擇模板,配置數(shù)據(jù)來動(dòng)態(tài)生成的,而其中類似商品樓層這種素材樓層,配置的素材數(shù)量也由運(yùn)營(yíng)自己決定,少的可能只有幾個(gè),多的幾十上百個(gè),這便導(dǎo)致通天塔首屏頁面有以下特點(diǎn) 1. 頁面靈活多變,頁面結(jié)構(gòu)難以預(yù)測(cè)。 2. 在請(qǐng)求首屏樓層數(shù)據(jù)時(shí),服務(wù)端難以計(jì)算需要下發(fā)幾個(gè)樓層剛好滿首屏,故按照素材樓層數(shù)來進(jìn)行分頁,如果首頁素材樓層配置的素材較多,節(jié)點(diǎn)數(shù)會(huì)非常龐大。 由于以上兩個(gè)特點(diǎn),導(dǎo)致很多活動(dòng)頁首屏的內(nèi)容,遠(yuǎn)大于客戶端首屏實(shí)際所需展示的長(zhǎng)度,這既加大了首屏的渲染耗時(shí),同時(shí)也浪費(fèi)了Node服務(wù)器的CPU資源(渲染了不必要的樓層)。 另外,在通過v8-profiler測(cè)試Node服務(wù)器性能時(shí),我們發(fā)現(xiàn)Node服務(wù)器端開銷最大的地方有三處- 通過JSON.parse解析后端下發(fā)的活動(dòng)數(shù)據(jù)
- React.renderToString 進(jìn)行首屏渲染
- JSON.stringify將首屏數(shù)據(jù)序列化后跟隨HTML下發(fā)給客戶端

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img data-src="/img-size.png" id="test" />
<h1>我是測(cè)試代碼</h1>
<script>
(function() {
var start = Date.now();
console.log('start', start)
var img = document.getElementById('test');
img.onload = function() {
console.log('end', Date.now(), Date.now() - start); // end - start > 2000ms
}
img.src = img.getAttribute('data-src');
})();
</script>
<h1>我是測(cè)試代碼22222</h1>
<img src="/performance-cpu.jpg" />
<script src="/test.js"></script>
</body>
</html>
其中test.js中很簡(jiǎn)單
console.log('start');
var start = Date.now();
for(;;) {
if (Date.now() - start > 2000) {
break;
}
}
console.log('end');

聲明:本文由網(wǎng)站用戶香香發(fā)表,超夢(mèng)電商平臺(tái)僅提供信息存儲(chǔ)服務(wù),版權(quán)歸原作者所有。若發(fā)現(xiàn)本站文章存在版權(quán)問題,如發(fā)現(xiàn)文章、圖片等侵權(quán)行為,請(qǐng)聯(lián)系我們刪除。