How We Cut H5 Page Load Time by 50%: A Deep Dive into Performance Optimization
This article details the systematic performance optimization of Penguin Tutor's H5 core pages, covering metric collection, analysis methods, environment setup, and concrete practices such as network tuning, code splitting, lazy loading, resource hints, and server‑side improvements that reduced total bundle size by 50% and cut onload time from over 6 seconds to around 1.2 seconds.
Penguin Tutor's H5 pages accumulated performance issues after years of iteration, causing slow page load and render speeds. This article summarizes a dedicated optimization project aimed at improving loading and rendering performance.
Project Background
The H5 project is a core component of Penguin Tutor, including course detail, teacher detail, enrollment, and payment pages, deployed to the app and various browsers. Over four years, performance regressions emerged, prompting a focused "H5 Performance Optimization" effort.
Performance optimization results
Performance metrics and data collection
Performance analysis methods and environment preparation
Specific optimization practices
Performance Metrics and Data Collection
The project tracks several key metrics:
First Contentful Paint (FCP) : time from page start to first content display.
Largest Contentful Paint (LCP) : time to render the largest text block or image.
DomContentLoaded Event: DOM parsing completion time.
OnLoad Event: full resource load time.
First Input Delay (FID) : delay from first user interaction to browser response.
Cumulative Layout Shift (CLS) : score of unexpected layout shifts.
Data is reported via IMLOG and monitored with an ELK stack in Grafana.
Performance Analysis and Environment Preparation
Initial observations showed a progress bar continuing for over ten seconds after the page was displayed, indicating a long onload time.
When navigation finishes, the render process loads resources and renders the page. Once the render process signals "finished" (all frames' onload events have fired), the UI thread stops the loading spinner.
Thus, reducing onload time directly shortens the progress bar duration. Chrome DevTools was used for analysis, focusing on Network, Performance, and Lighthouse panels.
1. Network Analysis
Testing under disabled cache and 4G/3G throttling revealed a DOMContentLoaded time of 6.03 s but an onload time of 20.92 s. The longest request during DOMContentLoaded was vendor.js (170 KB, 4.32 s).
Further inspection showed that onload was blocked by many media resources (images, videos, iframes).
2. Performance Analysis
Key Web Vitals (FP/FCP/LCP/CLS) and timings indicated late LCP and multiple layout shifts. Long tasks were observed, notably course.js taking up to 800 ms.
3. Lighthouse Analysis
Lighthouse scores were low, with TTI, SI, TBT, and LCP needing improvement. Diagnostics offered specific optimization suggestions such as image compression and removing unused JS.
Environment Preparation
Performance testing required realistic environment simulation using tools like Whistle, Charles, Fiddler for proxying, and local/nginx or STKE for server simulation. Data reporting tools (IMLOG, TAM, RUM) and bundle analysis tools (webpack‑bundle‑analyzer, rollup‑plugin‑visualizer) were employed.
Specific Optimization Practices
PART1: Loading Time Optimization
1. Critical JS Bundle Optimization
Vendor.js (170 KB gzipped) contained many modules that were only used by a few pages. By adjusting miniChunks rules and extracting truly shared dependencies into a separate common.js , vendor size dropped to 30 KB and common.js to 42 KB, achieving a 60% reduction.
<code>vendor: {
test({ resource }) {
return /[\\/]node_modules[\\/](@tencent\/imutils|imlog\/)|qqapi/.test(resource);
},
name: 'vendor',
priority: 50,
minChunks: 1,
reuseExistingChunk: true,
},
</code>2. On‑Demand Loading of Public Components
The Icon component (25 KB) was loaded on every page despite only eight icons being used. Switching to per‑icon imports via
babel-plugin-importreduced the Icon bundle from 74 KB to 20 KB (≈70% reduction).
3. Code Splitting of Business Components
Non‑critical sections such as "Course Outline", "Course Details", and "Purchase Notice" were split into separate chunks using
React.lazyor
loadable‑components, allowing delayed loading and smaller initial JS payload.
4. Tree Shaking
Ensured proper
sideEffectsconfiguration to avoid dead code in the final bundle.
2. Non‑Critical JS Deferred Loading
Reporting scripts (Sentry, beacon SDK) and captcha components were loaded after the page load using a prefetch check:
<code>const isPrefetchSupported = () => {
const link = document.createElement('link');
const { relList } = link;
if (!relList || !relList.supports) { return false; }
return relList.supports('prefetch');
};
const prefetch = () => {
if (isPrefetchSupported()) {
const link = document.createElement('link');
link.rel = 'prefetch';
link.as = type;
link.href = url;
document.head.appendChild(link);
} else if (type === 'script') {
// load script
}
};
</code>This prevented non‑essential scripts from blocking the onload event.
3. Media Resource Loading Optimization
3.1 Load Sequencing
Images and videos not visible on initial render were lazy‑loaded using viewport detection (getBoundingClientRect).
3.2 Size Optimization
Large images (e.g., 1715 px width) were served via CDN with dynamic resizing (srcset/sizes) and format selection, achieving up to 13× size reduction on weak networks.
3.3 Other Resources
Iframes were deferred until after onload using setTimeout, and data‑reporting images were delayed or sent via Beacon API to avoid blocking the load event.
PART2: Rendering Optimization
1. TTFB Reduction
Analysis showed that the NGW gateway and STKE service were in different regions, causing ~120 ms network latency. Deploying the gateway and service in the same region reduced average gateway time from 153 ms to 31 ms (≈80% improvement).
2. CSS Rendering Optimization
Only 15% of CSS was used on the first screen. Critical CSS was inlined using
critters, allowing the page to render before full CSS download.
3. Layout Shift Mitigation
Fixed element dimensions, used base64 for small images, and ensured dynamic content did not alter layout unexpectedly.
Optimization Results
Key improvements:
Bundle size reduced from 460.8 KB to 308 KB (≈50%).
Largest file size reduced from 170 KB to 109 KB (≈56%).
Onload time decreased from 6.18 s to 1.19 s (≈81% reduction).
Lighthouse performance score increased from ~45 to ~80 (≈47% boost).
Progress‑bar load time dropped from 4632 ms to 2581 ms (≈45% improvement).
Conclusion and Future Plans
Further work includes PWA adoption, skeleton screens for non‑direct pages, and CSR‑to‑SSR migration.
CDN migration is planned to reduce download latency.
Continuous performance monitoring will be integrated into the development workflow.
Page‑specific optimizations require proactive developer involvement.
Thank you for reading; feedback and discussion are welcome.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
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.