阿里云主机折上折
  • 微信号
Current Site:Index > Best practices for code splitting

Best practices for code splitting

Author:Chuan Chen 阅读数:55584人阅读 分类: 构建工具

Performance optimization is one of the core concerns of modern frontend development, and code splitting is a key technique for improving application loading speed and runtime efficiency. Vite.js, as a next-generation build tool, provides a more efficient implementation of code splitting thanks to its native ESM support and on-demand compilation features.

Basic Principles of Code Splitting

The core idea of code splitting is to break down application code into smaller chunks that can be loaded on demand or in parallel. Traditional bundling tools like Webpack achieve splitting through dynamic import(), while Vite leverages native browser ESM for finer-grained control.

Browser-native dynamic import syntax:

// Dynamically import a component
const module = await import('./module.js')

Vite extends this with the following features:

  • Automatic route-based splitting
  • Dependency pre-bundling optimization
  • CSS code splitting
  • Async chunk loading strategies

Route-Level Code Splitting in Practice

In single-page applications, routes are the most natural splitting points. Vite, combined with modern frontend frameworks, enables automatic route-level splitting.

React project example:

// Use React.lazy for route splitting
const Home = React.lazy(() => import('./views/Home'))
const About = React.lazy(() => import('./views/About'))

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  )
}

Vue project example:

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router']
        }
      }
    }
  }
})

Fine-Grained Component-Level Splitting

For large components, further component-level splitting strategies can be implemented.

React higher-order component splitting example:

const HeavyComponent = React.lazy(() => import(
  /* webpackChunkName: "heavy-component" */
  './HeavyComponent'
))

function ParentComponent() {
  const [showHeavy, setShowHeavy] = useState(false)
  
  return (
    <div>
      <button onClick={() => setShowHeavy(true)}>
        Load Heavy Component
      </button>
      {showHeavy && (
        <Suspense fallback={<div>Loading...</div>}>
          <HeavyComponent />
        </Suspense>
      )}
    </div>
  )
}

Third-Party Library Splitting Strategy

Third-party libraries are often large, and proper splitting can significantly improve performance.

Manual vendor splitting configuration:

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('lodash')) {
              return 'vendor-lodash'
            }
            if (id.includes('moment')) {
              return 'vendor-moment'
            }
            return 'vendor'
          }
        }
      }
    }
  }
})

Advanced Usage of Dynamic Imports

Vite supports more flexible dynamic import methods, including dynamic imports with variables.

Dynamic import with template literals:

async function loadLocale(lang) {
  return await import(`./locales/${lang}.json`)
}

Dynamic import with preload hints:

// Preload resource
const module = import('./module.js')
// Use later
const result = await module

CSS Code Splitting Optimization

Vite automatically extracts CSS into separate files, but we can optimize further.

Configuration example:

// vite.config.js
export default defineConfig({
  css: {
    modules: {
      localsConvention: 'camelCase'
    },
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      }
    }
  }
})

Component-level CSS splitting:

<!-- Vue SFC example -->
<style module>
/* Component-scoped CSS */
</style>

Preload Directive Optimization

Vite automatically generates preload directives, but sometimes manual control is needed.

Manual preload in HTML:

<link rel="modulepreload" href="/src/components/CriticalComponent.js" />

Adjusting preload strategy via configuration:

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          // Configuration omitted...
        },
        experimentalMinChunkSize: 10000 // Set minimum chunk size
      }
    }
  }
})

Loading State Management for Async Loading

A good loading state experience is crucial for users.

React example:

function AsyncWrapper({ children }) {
  const [isLoading, setLoading] = useState(false)
  
  useEffect(() => {
    const handler = () => setLoading(true)
    window.addEventListener('beforeimport', handler)
    return () => window.removeEventListener('beforeimport', handler)
  }, [])

  return (
    <div>
      {isLoading && <ProgressBar />}
      {children}
    </div>
  )
}

Production Environment Analysis and Tuning

Using rollup-plugin-visualizer to analyze bundle size:

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true
    })
  ]
})

Optimization strategies based on analysis results:

  1. Consider alternatives for oversized third-party libraries
  2. Deduplicate repeated dependencies
  3. Replace non-essential dependencies with dynamic imports

Code Splitting in Server-Side Rendering

SSR scenarios require special handling.

Vue SSR example:

// Server entry
export async function createApp() {
  const { createApp } = await import('vue')
  const App = await import('./App.vue')
  return createApp(App)
}

React SSR example:

// Use @loadable/component for SSR splitting
import loadable from '@loadable/component'

const AsyncComponent = loadable(() => import('./Component'))

Performance Monitoring and Continuous Optimization

Implement automated performance monitoring:

// Use web-vitals library to monitor performance
import { getCLS, getFID, getLCP } from 'web-vitals'

function sendToAnalytics(metric) {
  console.log(metric)
}

getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getLCP(sendToAnalytics)

Optimization based on real user data:

  1. Analyze high-latency routes
  2. Identify slow-loading third-party resources
  3. Monitor code splitting effectiveness

Common Issues and Solutions

Issue 1: Flickering caused by dynamic imports
Solution: Use skeleton screens or preloading strategies

Issue 2: Too many chunks causing HTTP/2 head-of-line blocking
Solution: Reasonably merge small chunks

Issue 3: Dynamic import path issues
Solution: Use Vite's import.meta.glob

const modules = import.meta.glob('./dir/*.js')

Issue 4: SSR hydration mismatch
Solution: Ensure consistent splitting strategies between client and server

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.