
1
首屏加載性能 首屏加載耗時,TP75從原來的2.47秒減小到了1.58秒,性能提升約36.03%。圖2所示為優化前后以周為維度的首屏TP75加載時長。
2
服務器性能 通過對服務器進行壓測,在相同QPS維度下,CPU從原來的29.52%降低至20.5%,CPU使用率相比之前降低了30.5%
1
Performance API
Performance API是ECMAScript5才引入的,精度可達到1毫秒的千分之一,目前主流瀏覽器基本都已經支持performance對象。通過performance.timing對象,可以拿到瀏覽器處理網頁各個階段的耗時。通過performance.getEntries方法,可以獲取js, css, 圖片及ajax在內的所有請求的耗時信息。 我們基于Performance API,封裝了一個前端測速模塊,該模塊在頁面加載完成后將所需性能數據上報至服務器,之后可以在可視化平臺上進行數據的展示及分析。2
Chrome Devtools
Chrome Devtools是前端調試及性能分析常用的工具,通過該工具,可以查看頁面資源加載情況,所加載CSS、JS及圖片的大小,還可以通過Performance面板,查看頁面渲染繪制和Script執行情況。3
v8-profiler
通天塔H5是基于React SSR架構的,頁面首屏在Node中間層請求數據并渲染,所以除純前端的監控及分析,還需對Node層進行性能分析及優化。在Node層性能分析中,我們主要通過v8-profiler模塊進行性能分析。 在本地或測試環境下新增兩個路由: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壓測工具,對服務器發起請求
ab -c 10 -n 1000 http://localhost:7001/mall/active/xxx/index.html
在壓測過程中,可以通過 http://localhost:7001/profiler/start開始性能統計,通過http://localhost:7001/profiler/end結束性能統計,并將結果保存為 ***.cpuprofile文件,通過Chrome Devtools中的JavaScript Profiler工具進行分析。
1
前端常規優化 在優化之初,根據《高性能網站建設指南》提及的常規優化方案,我們檢查了項目中需要改進或深度優化的地方,主要涉及以下方面:- 盡可能的減少HTTP的請求數,減小HTTP請求大小
- 將靜態資源放在CDN,最大化利用CDN緩存能力
- 減少CSS和JS請求個數,減小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緩存
在做性能優化前,通天塔靜態資源的打包,是開發者在上線前,在自己電腦上進行的,且文件名會依據文件內容重新生成,格式為[filename].[contenthash:8].js。
按這種方式在個人電腦上打包,即使有package-lock.json鎖定包版本,但由于個人電腦操作系統及使用的npm包管理工具的不同(有的包管理工具不讀package-lock.json),node_module下的文件可能會不一致,導致文件的contenthash不同。
針對這個問題,我們基于Jenkins搭建了一個前端CI打包系統,后繼所有上線前的前端靜態資源打包,都遷移到CI上進行,通過這種方式,確保了文件名的一致性,以最大程度的利用CDN緩存。
3)調整webpack打包策略,按需加載CSS和JS
在性能優化前,通天塔的CSS和JS資源是按以下策略打包的:
- vendor.[contenthash:8].js: 包含node_module下的代碼
- common.[contenthash:8].js: 包含非node_modules下的代碼
- [channel].[contenthash:8].js: 通天塔有很多渠道,每個渠道的專屬代碼打包到這個JS中
- template.[contenthash:8].css: 包含所有渠道通用CSS
- [channel].[contenthash:8].css: 包含渠道專有CSS
- vendor.[contenthash:8].js: 包含node_module下的JS文件
- lowUsedTemp.[contenthash:8].js: 包含使用頻率低的系統模板代碼,頁面會按照活動是否使用到低頻模板按需請求
- mute.[contenthash:8].js: 包含剔除低頻使用模板后,較為穩定,很少改動的系統模板
- template.[contenthash:8].js: 包含剩余非node_modules下的代碼
- [channel].[contenthash:8].js: 包含渠道專屬代碼
- lowUsedTemp.[contenthash:8].css: 包含使用頻率低的系統模板代碼的CSS,頁面會按照活動是否使用到低頻模板按需請求
- template.[contenthash:8].css: 包含所有渠道通用CSS
- [channel].[contenthash:8].css: 包含渠道專有CSS
2
業務優化 在常規的前端性能優化達到瓶頸后,我們開始嘗試基于業務進行性能優化。 1)首屏精準化優化 通天塔頁面是運營在可視化配置平臺中,通過選擇模板,配置數據來動態生成的,而其中類似商品樓層這種素材樓層,配置的素材數量也由運營自己決定,少的可能只有幾個,多的幾十上百個,這便導致通天塔首屏頁面有以下特點 1. 頁面靈活多變,頁面結構難以預測。 2. 在請求首屏樓層數據時,服務端難以計算需要下發幾個樓層剛好滿首屏,故按照素材樓層數來進行分頁,如果首頁素材樓層配置的素材較多,節點數會非常龐大。 由于以上兩個特點,導致很多活動頁首屏的內容,遠大于客戶端首屏實際所需展示的長度,這既加大了首屏的渲染耗時,同時也浪費了Node服務器的CPU資源(渲染了不必要的樓層)。 另外,在通過v8-profiler測試Node服務器性能時,我們發現Node服務器端開銷最大的地方有三處- 通過JSON.parse解析后端下發的活動數據
- React.renderToString 進行首屏渲染
- JSON.stringify將首屏數據序列化后跟隨HTML下發給客戶端

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img data-src="/img-size.png" id="test" />
<h1>我是測試代碼</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>我是測試代碼22222</h1>
<img src="/performance-cpu.jpg" />
<script src="/test.js"></script>
</body>
</html>
其中test.js中很簡單
console.log('start');
var start = Date.now();
for(;;) {
if (Date.now() - start > 2000) {
break;
}
}
console.log('end');

聲明:本文由網站用戶香香發表,超夢電商平臺僅提供信息存儲服務,版權歸原作者所有。若發現本站文章存在版權問題,如發現文章、圖片等侵權行為,請聯系我們刪除。