Performance Monitoring and Optimization of SSR Applications in Ctrip Financial Frontend
This article describes how Ctrip Financial’s front‑end team measured, analyzed, and optimized key web‑performance metrics such as First Contentful Paint, DNS/TCP durations, and Cumulative Layout Shift for their server‑side rendered applications, providing concrete monitoring code, data‑processing methods, and practical optimization techniques to improve user experience and achieve a 70%+ instant‑load rate.
Background – In the post‑Internet era, user experience determines user retention; a page load increase from 1 s to 3 s raises bounce rate by 32 %, and to 6 s raises it by 106 %. Ctrip Financial’s front‑end team therefore implemented a comprehensive performance governance for their internal SSR applications.
1. Performance Metrics
User experience is quantified through two core Web Vitals: First Contentful Paint (FCP) and Cumulative Layout Shift (CLS). Monitoring these metrics enables objective measurement and continuous improvement.
2. Performance Monitoring
Two monitoring approaches are used: Synthetic Monitoring (SYN) and Real‑User Monitoring (RUM). The team adopts RUM to capture real user interactions.
2.1 First‑Screen Time
Key timing points collected:
DNSDuration – DNS lookup time
TCPDuration – TCP connection time
TTFBDuration – time to first byte
RequestDuration – SSR server processing time
FCP – first contentful paint
DOMContentLoaded – app‑specific first‑screen time
Load – total resource load time
Target P90 values:
RequestDuration ≤ 300 ms
FCP ≤ 800 ms
DOMContentLoaded ≤ 1000 ms
Data collection uses PerformanceObserver on Android and Performance.timing on iOS.
// 创建性能监测实例
function createPerformanceObserver(callback) {
const po = new window.PerformanceObserver(list => {
callback && callback(list)
})
return po
}
// 性能监测处理函数
function performationAduitCallback(list) {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
const DNSDuration = entry.domainLookupEnd - entry.domainLookupStart
const TCPDuration = entry.connectEnd - entry.connectStart
const RequestDuration = entry.responseEnd - entry.requestStart
const TTFBDuration = entry.responseStart - entry.startTime
const DOMCompleteDuration = entry.domContentLoadedEventEnd - entry.startTime
const DOMLoadDuration = entry.duration
let FCPDuration = 0
if (performance && performance.timing) {
FCPDuration = window.headReadyTime ? window.headReadyTime - performance.timing.navigationStart : 0
}
const performanceParams = Object.assign({}, extraInfo, {
DNSDuration,
TCPDuration,
RequestDuration,
TTFBDuration,
FCPDuration,
DOMCompleteDuration,
DOMLoadDuration,
})
sendPerformance(performanceParams)
}
}
}
function performationAduitAndroid() {
try {
const PO = createPerformanceObserver(performationAduitCallback)
PO.observe({ entryTypes: ['navigation'] })
} catch (e) {
console.log('performationAduitAndroid error', e)
}
} // iOS version
function performationAduitIos() {
const timing = performance && performance.timing
if (!timing) return
const start = timing.navigationStart
const param = {
DNSDuration: timing.domainLookupEnd - timing.domainLookupStart,
TCPDuration: timing.connectEnd - timing.connectStart,
RequestDuration: timing.responseEnd - timing.requestStart,
TTFBDuration: timing.responseStart - start,
FCPDuration: window.headReadyTime ? window.headReadyTime - start : 0,
DOMCompleteDuration: timing.domContentLoadedEventEnd - start,
DOMLoadDuration: timing.loadEventEnd - start,
}
const performanceParams = Object.assign({}, extraInfo, param)
sendPerformance(performanceParams)
}2.2 Cumulative Layout Shift (CLS)
CLS measures visual stability; a target P90 of 0.05 is set. Collection also uses PerformanceObserver (Android only).
// CLS handling
function clsAduitCallback(list) {
for (const entry of list.getEntries()) {
if (entry.entryType === 'layout-shift') {
const CLS = entry.value ? entry.value * 1000 : 0
const CLSParams = Object.assign({}, extraInfo, { CLS })
sendPerformance(CLSParams)
}
}
}
function clsAduit() {
try {
const clsPO = createPerformanceObserver(clsAduitCallback)
if (window.PerformanceObserver.supportedEntryTypes.includes('layout-shift')) {
clsPO.observe({ type: 'layout-shift', buffered: true })
}
window.addEventListener('load', () => {
setTimeout(() => clsPO.disconnect(), 1000)
})
} catch (e) {
console.log('cls error=>', e)
}
}3. Data Processing and Analysis
Collected metrics are aggregated and analyzed using percentile (P50, P90) calculations and segmented by client (Ctrip, Ctrip Finance, Qunar). The “instant‑load rate” is defined as the proportion of sessions where First‑Screen Time < 1 s.
4. Optimization Roadmap
Based on the analysis, the team applied targeted optimizations:
DNSDuration : selective DNS prefetch for domains not present on the initial page.
TCPDuration : keep homepage HTML < 14 KB to fit in a single TCP packet; migrate from HTTP/1 to HTTP/2 for multiplexing.
RequestDuration : speed up backend APIs and defer non‑essential calls to asynchronous client requests.
FCP : inline critical CSS.
DOMContentLoaded (DCL) : preload blocking JS, split bundles into async chunks, extract common chunks, use content hashing for cache efficiency.
Onload : cache low‑impact API data, convert images to WebP, limit image size, preload fonts.
CLS : avoid layout shifts, limit shift distance, use skeleton screens and animations.
5. Conclusion
After implementing these measures, the instant‑load rate of the financial SSR applications exceeded 70 %, and real‑time monitoring now promptly detects performance regressions, enabling rapid remediation and delivering a smoother experience for all users.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.