Frontend Development 12 min read

Frontend Performance Optimization: Common Issues and Solutions for Large‑Scale Projects

Large‑scale front‑end projects suffer from oversized bundles, unnecessary listeners, deep cloning, and mutable state, causing latency and crashes; the article explains how to diagnose these problems with Chrome DevTools and Webpack tools and resolves them through bundle splitting, tree‑shaking, memoisation, immutable patterns, and caching.

Amap Tech
Amap Tech
Amap Tech
Frontend Performance Optimization: Common Issues and Solutions for Large‑Scale Projects

As front‑end applications grow, performance problems become critical, especially in large, complex projects where a small data dependency can cause page jank or crashes.

The article, based on the evolution of the Quick BI data‑visualisation platform, summarises common front‑end performance issues and provides practical solutions.

Typical causes

Oversized resource bundles (tens of megabytes) leading to long download times on weak networks and slow parsing.

Unnecessary data‑flow listeners (e.g., hooks + redux patterns) that trigger re‑renders even when only a small part of the state changes.

Heavy use of _.cloneDeep or similar deep‑clone utilities that duplicate large objects.

Mutable data that is hard to trace, causing unpredictable re‑renders.

These issues manifest as increased first‑paint latency, higher CPU usage, and in extreme cases browser crashes.

Diagnosis tools

Chrome DevTools Network tab to inspect bundle size.

Chrome DevTools Performance flame graph to locate long‑running tasks.

Webpack stats (via webpack --profile --json > ./build/stats.json ) and webpack-bundle-analyzer to visualise module composition.

Chrome Coverage to identify dead code.

React Profiler to count component renders.

Example of a large bundle analysis command:

webpack --profile --json > ./build/stats.json
webpack-bundle-analyzer ./build/stats.json

Remediation strategies

1. Bundle analysis and splitting

Use splitChunks in Webpack 4 (or later) to create shared chunks and limit parallel requests. Example configuration:

module.exports = {
    //...
    optimization: {
        splitChunks: {
            chunks: 'async',
            minSize: 20000,
            minRemainingSize: 0,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 30,
            maxInitialRequests: 30,
            automaticNameDelimiter: '~',
            enforceSizeThreshold: 50000,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

Ensure third‑party libraries larger than 400 KB are loaded asynchronously (dynamic import() ).

2. Tree‑shaking and side‑effect handling

Mark packages as side‑effect‑free in package.json ( "sideEffects": false ) or configure module.rules accordingly. Avoid export * as … syntax which can break tree‑shaking.

3. Reduce unnecessary data‑flow listeners

Wrap components with React.memo and memoise expensive calculations with useMemo or React.useMemo . Example:

const Foo = () => { // 1. React.memo can avoid re‑render when props unchanged
    const result = calc(); // 2. Move heavy logic into useEffect or useMemo
    return
; // 3. useMemo around render output if needed
};

4. Avoid deep cloning of large objects

Replace _.cloneDeep with more selective copying or immutable libraries that share unchanged sub‑structures. Example of a problematic clone:

// a.tsx
export const a = {
    name: 'a',
};
// b.tsx
import { a } = b;
saveData(_.cloneDeep(a)); // deep clone before persisting

Consider custom immutable helpers that only clone the parts that actually change.

5. Detect and control mutable data

Use Object.freeze during debugging to surface illegal mutations:

const obj = {
    prop: 42
};
Object.freeze(obj);
obj.prop = 33; // Throws in strict mode

Or employ a proxy‑based watcher:

const a = { b: { c: { d: 123 } } };
watchObject(a);
const c = a.b.c;
c.d = 0; // Prints: Modify: "a.b.c.d"

These techniques reveal where a property is read, written, and the call stack that performed the change.

6. Cache expensive results

Store computation results in WeakMap or similar caches to avoid repeated work.

Overall, the article stresses a “performance‑first” mindset: keep bundles small, eliminate dead code, minimise mutable state, and leverage memoisation and caching throughout the React/Redux stack.

FrontendPerformanceoptimizationReactWebpacktree-shakingmutable-data
Amap Tech
Written by

Amap Tech

Official Amap technology account showcasing all of Amap's technical innovations.

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.