前端代码分割:别再让你的用户加载不必要的代码了
各位前端同行,咱们今天聊聊前端代码分割。别告诉我你还在把所有代码打包成一个文件,那感觉就像让用户下载整个图书馆只为看一本书——完全没必要。
为什么你需要代码分割
最近看到一个项目,把所有代码打包成一个 5MB 的文件,首屏加载时间能达到 3 秒,我差点当场去世。我就想问:你是在开发应用还是在测试用户的网络速度?代码分割可以显著减少首屏加载时间,提高用户体验。
反面教材
// 反面教材:没有代码分割
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import HeavyComponent from './HeavyComponent'; // 1MB 的重量级组件
import AnotherHeavyComponent from './AnotherHeavyComponent'; // 800KB 的重量级组件
import Chart from './Chart'; // 500KB 的图表组件
import Editor from './Editor'; // 1.2MB 的编辑器组件
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
毒舌点评:这代码,我看了都替你的用户着急。把所有代码打包成一个文件,你是想让用户的浏览器崩溃吗?用户打开首页,却要加载所有页面的代码,这得多慢啊?
前端代码分割的正确姿势
1. 使用动态导入
动态导入是 ES6 引入的特性,可以在运行时按需加载模块。
// 正确姿势:使用动态导入
import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AnotherHeavyComponent = lazy(() => import('./AnotherHeavyComponent'));
const Chart = lazy(() => import('./Chart'));
const Editor = lazy(() => import('./Editor'));
ReactDOM.render(
<React.StrictMode>
<App>
<Suspense fallback={<div>Loading…</div>}>
<HeavyComponent />
<AnotherHeavyComponent />
<Chart />
<Editor />
</Suspense>
</App>
</React.StrictMode>,
document.getElementById('root')
);
毒舌点评:早这么做,你的应用早流畅了。按需加载组件,用户打开首页时只加载首页需要的代码,这多快啊?
2. 基于路由的代码分割
基于路由的代码分割是最常见的代码分割方式,每个页面只加载自己需要的代码。
// 正确姿势:基于路由的代码分割
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
const Contact = lazy(() => import('./Contact'));
const Dashboard = lazy(() => import('./Dashboard'));
const Admin = lazy(() => import('./Admin'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading…</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/admin" element={<Admin />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;
毒舌点评:这才是正确的姿势!用户访问哪个页面,就加载哪个页面的代码,其他页面的代码不加载,这多高效啊?
3. 配置 Webpack 分割代码
配置 Webpack 的 splitChunks 选项,可以将第三方库和公共代码分割成单独的文件。
// 正确姿势:配置 Webpack 分割代码
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\\\/]node_modules[\\\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true,
},
},
},
},
};
毒舌点评:早这么做,你的第三方库代码早被缓存了。用户再次访问你的应用时,不需要重新加载第三方库代码,这多快啊?
4. 基于条件的代码分割
根据用户的操作或条件,按需加载代码。
// 正确姿势:基于条件的代码分割
import React, { useState } from 'react';
function App() {
const [showChart, setShowChart] = useState(false);
const [chartComponent, setChartComponent] = useState(null);
const loadChart = async () => {
const { default: Chart } = await import('./Chart');
setChartComponent(<Chart />);
setShowChart(true);
};
return (
<div>
<button onClick={loadChart}>Show Chart</button>
{showChart && chartComponent}
</div>
);
}
export default App;
毒舌点评:这才是聪明的做法!用户需要什么功能,才加载什么代码,这多节省带宽啊?
实战技巧:前端代码分割指南
前端代码分割不是可选的,是必须的。别再让你的用户加载不必要的代码了——分割一下,你的应用会更流畅,用户体验会更好。



