Cut Page Load to 1 Second: Frontend Performance Strategies for Feitian Service Platform
Facing heavy load times on the Feitian Service Platform’s customizable homepage, the team applied a three‑stage front‑end performance overhaul—shrinking resources, prioritizing critical paths with progressive loading, and leveraging caching via SWR and Service Workers—resulting in sub‑second page renders and a smoother user experience.
As Small As Possible
Resource Tree Comparison Before and After Optimization
Before optimization the resource bundle was large; after separating third‑party libraries and applying treeshaking the vendor size dropped from 1.42 MB to 384 KB.
Key techniques:
Bundle common libraries separately
Code‑split by route
Load Ant Design theme and fonts on demand
Dependency Treeshaking
Configure sideEffects in package.json to false when a package has no side effects. "sideEffects": ["*.less"] After treeshaking each page or card can be bundled independently, reducing unnecessary code and further shrinking bundle size.
Drawer Component Separate Bundling
Dynamic loading with a skeleton placeholder reduces initial load time for heavy drawer components.
/**
* Async load ProjectDetailCard component
*/
import { dynamic } from 'umi';
import { Skeleton } from 'antd';
const ProjectDetailCard = dynamic({
loading: () => (
<Skeleton active paragraph={{ rows: 10 }} />
),
async loader() {
const { ProjectDetailCard } = await import(/* webpackChunkName: "ProjectDetailCard" */ './index');
return ProjectDetailCard;
},
});
export { ProjectDetailCard };Critical Path First, Non‑Critical Progressive Loading
Image Lazy Loading
<img src="image.png" loading="lazy" alt="..." width="200" height="200">Browsers natively support lazy loading, providing a high‑ROI performance gain.
Using Browser Idle Time
Insert low‑priority work via window.requestIdleCallback so that background tasks do not block critical interactions.
if (typeof window.requestIdleCallback !== 'undefined') {
const id = window.requestIdleCallback(() => {
// render idle components
});
} else {
setTimeout(() => {/* fallback */});
}This technique delays rendering of non‑essential components (e.g., pop‑up dialogs) until the browser is idle, reducing the initial unresponsive period.
1.5‑Screen Scroll Rendering
Use the Intersection Observer API (or the react-intersection-observer library) to lazily load cards when they approach the viewport, providing a smoother experience than loading all cards at once.
Cache: Space‑for‑Time Trade‑off
Global Skeleton Screens
Two skeleton strategies are applied: one for card loading and another for tables, giving users an immediate visual cue while data loads.
SWR (Stale‑While‑Revalidate)
SWR returns cached data first, then revalidates in the background, ensuring UI stays fast and up‑to‑date.
import useSWR from 'swr';
const { data, error } = useSWR('/api/data', fetcher);Data is also persisted in IndexedDB, allowing an initial render from local cache before the network response arrives.
Service Worker Resource Caching
A Service Worker serves cached assets first, falls back to the network, and then caches the network response for future requests.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
const copy = response.clone();
caches.open('v1').then(cache => cache.put(event.request, copy));
return response;
});
})
);
});Summary and Outlook
By minimizing resource size, prioritizing critical paths, progressively loading non‑critical assets, and fully leveraging caching (SWR and Service Workers), the Feitian Service Platform homepage now loads in about one second, delivering a near‑native experience. Further optimizations can continue to push performance toward the theoretical limits.
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.
