Frontend Development 10 min read

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.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Performance Monitoring and Optimization of SSR Applications in Ctrip Financial Frontend

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.

frontendmonitoringPerformanceoptimizationJavaScriptssrweb-vitals
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.