How We Halved Cloud Music Desktop Startup Time and Fixed UI Lag with a React Refactor

This article details the migration of the Cloud Music desktop client from a legacy NEJ‑CEF hybrid to a React‑based architecture, outlines four major performance challenges, and explains the step‑by‑step optimizations—including API preloading, render memoization, virtual‑list replacement, and resource‑usage reductions—that cut startup latency by 48%, eliminated interaction stutter, and dramatically lowered CPU, GPU, and memory consumption.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
How We Halved Cloud Music Desktop Startup Time and Fixed UI Lag with a React Refactor

Background

The Cloud Music desktop client was built in 2014 with a NEJ + CEF hybrid architecture. Over time the NEJ front‑end became hard to maintain, lacked community support and slowed feature delivery. For the 3.0 redesign the team rewrote the entire UI in React to enable modern interaction patterns, visual designs and lower development cost.

Key Challenges

More than 40 pages and dozens of windows required >30 base components and >100 business components, leading to expensive renders for complex items such as playlist rows.

Playlists can contain thousands of tracks, demanding virtual‑list rendering, fast scrolling and low memory usage.

Over 90 global functions and event types (right‑click, double‑click, keyboard, etc.) were routed through a unified dispatch center, causing massive re‑render cascades as the component tree grew.

>50 global state models (playback, download, user, playlist, config, ABTest, …) resulted in many view subscriptions, inflating render time when unrelated state changed.

1. Playback Start‑up Optimization

1.1 API Pre‑loading

When a user opens a playlist the app now pre‑loads all paginated song data during idle time before playback begins, eliminating network latency from the critical start‑up path.

1.2 Render Optimizations

Render cost was reduced by:

Splitting the monolithic TableIndex component into smaller memoized pieces ( IndexCell, TrackTitleCell) and wrapping them with React.memo that performs a deep prop comparison.

Replacing CSS‑in‑JS styled components with plain div elements and CSS variables, removing unnecessary component layers.

Result: for a 1000‑track playlist the start‑up time dropped from ~4410 ms to ~2133 ms (≈48% improvement).

2. Interaction Lag Optimization

2.1 Replace Declarative Event Provider

function Demo() {
  const onClick = useCallback(e => {
    doAction({
      click: currentAction,
      data: { resource, resourceType, from: from ?? {} },
      event: e
    });
  }, [currentAction, resource, resourceType, from]);

  return <TrianglePlayButtonWrapper onClick={onClick}>…</TrianglePlayButtonWrapper>;
}

The ActionProvider pattern was removed and direct callbacks were used. This kept the automatic dispatch logic while eliminating the heavy React.cloneElement and re‑render path. Subjective UI scores rose from 3.2 to 4.2, matching the previous version.

2.2 Virtual List Refactor

The custom virtual list suffered from large re‑render scopes and an immature implementation. It was replaced with react‑virtualized 's VirtualizedList, adding:

Window‑level scrolling via WindowScroller.

Skeleton placeholders powered by react‑content‑loader (animation disabled by default).

Accurate scrollToIndex logic based on offsetHeight, scrollTop and row height.

The component hierarchy was simplified by removing TableViewerMain and applying a “minimum re‑render unit” principle, isolating playback‑position components. Result: list‑scroll lag score improved from 2.2 to 4.0 (≈68% reduction in negative feedback).

3. System Resource Optimization

3.1 CPU – On‑Demand Animations

CSS keyframe animations (e.g., the rotating vinyl) added ~6 % CPU load. A custom hook monitors window visibility and pauses animations when the app is hidden.

const useWindowShow = () => {
  const [isWindowShow, setIsWindowShow] = useState(true);
  useEffect(() => {
    const sub = windowStateChange$.subscribe(({ isShow }) => setIsWindowShow(isShow));
    return () => sub.unsubscribe();
  }, []);
  return [isWindowShow, setIsWindowShow];
};

Background CPU usage fell from 7 % to 0.74 %.

3.2 GPU – Backdrop‑filter & Off‑screen DOM

Global backdrop-filter (grayscale + blur) combined with continuous animations caused GPU usage to climb to 33 %. The team disabled the global filter, removed the blur from the bottom bar, and hid off‑screen pages with visibility:hidden (instead of display:none) to keep layout caches while preventing rendering. GPU usage dropped to ~5 %.

3.3 Memory – Leak Fixes

Memory growth was traced to a tracking SDK that stored references to detached DOM nodes in a NE_DAWN_CHILDREN property via a MutationObserver. Clearing these references on node removal stopped the leak, stabilising memory consumption and eliminating related user complaints.

4. Future Work

Integrate web‑vitals monitoring for core pages and playback flow to guard against performance regression.

Explore a custom drawing UI (e.g., Flutter + DSL) to combine rapid development with native‑level experience.

Regularly update the CEF Chromium base (currently v91) to benefit from engine improvements.

Re‑architect the playback pipeline to async‑ify heavy tasks and further shrink start‑up latency.

Conclusion

The systematic refactor—API pre‑loading, component memoization, replacement of a custom virtual list with react‑virtualized, and targeted CPU/GPU/memory optimisations—demonstrated that data‑driven performance engineering can dramatically improve a large‑scale Electron‑based desktop application’s responsiveness and user satisfaction.

performance optimizationHybrid AppReActCPUGPUMemoryVirtual List
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

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.