我做 react 开发时通常是直接用的 create-react-app。最近想分析一下一个用 create-react-app 开发的项目的打包结果,看看有没有什么可以优化的地方。
项目情况
执行 npm run eject
导出配置(单向操作,不可逆)。
项目中使用的一些库:
"dependencies": {
"antd": "^3.9.2",
"axios": "^0.18.0",
"echarts": "^3.8.5",
"less": "^3.0.1",
"moment": "^2.21.0",
"react": "^16.4.2",
"react-dom": "^16.2.0",
"react-router-dom": "^4.2.2",
},
"devDependencies": {
"babel-loader": "7.1.2",
"babel-plugin-import": "^1.6.5",
"webpack": "3.8.1",
"webpack-bundle-analyzer": "3.0.2",
}
antd 按需加载
按照官网配置,使用 babel-plugin-import,在 package.json 中配置:
"babel": {
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": true
}
]
]
}
项目中直接使用:
import { Button } from 'antd';
Moment.js locale 打包优化
create-react-app 的 webpack 已经做好配置了:
plugins: [
...
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
...
]
router component 懒加载
import React from 'react';
export default class Bundle extends React.Component {
state = {
mod: null
}
componentWillMount() {
this.load(this.props);
}
componentWillReceiveProps(nextProps) {
if (nextProps.load !== this.props.load) {
this.load(nextProps);
}
}
async load(props) {
this.setState({
mod: null
});
/*
使用 props.load() 返回的是一个 promise
*/
const mod = await props.load();
this.setState({
mod: mod.default ? mod.default : mod
});
}
render() {
return this.state.mod ? this.props.children(this.state.mod) : null;
}
}
const lazyLoad = loadComponent => props => (
<Bundle load={loadComponent}>
{Comp => (Comp ? <Comp {...props} /> : <Loading/>)}
</Bundle>
);
使用:
// dynamic import
const Demo = lazyLoad(() => import('../components/demo'));
// react-router
<Route path="/demo" component={Demo} />
分析项目打包情况
使用工具webpack-bundle-analyzer,将打包内容转换成可缩放的树状图。
// 安装
yarn add -D webpack-bundle-analyzer
在 config/webpack.config.dev.js
或 config/webpack.config.prod.js
中添加:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
...
plugins: [
...
new BundleAnalyzerPlugin(),
...
]
...
默认在 127.0.0.1:8888
页面显示。
添加在 config/webpack.config.dev.js
后,每次 npm start
时都会弹出分析页面。
添加在 config/webpack.config.prod.js
后,每次 npm build
时都会弹出分析页面。
项目分析结果:
发现问题:
- 多个 chunk 中重复打包了相同的 antd 组件代码(table, pagination, .etc)。
- echarts 全部打包了进来,体积太大。
优化
antd 组件代码重复打包问题
由于我对 webpack 不算太熟,知道 webpack 3 中可以用 plugin CommonsChunkPlugin
抽取公共代码,但具体用法不甚明了,折腾了很久都没有作用,终于在 antd github 的 issues 中找到答案:
plugins: [
...
new webpack.optimize.CommonsChunkPlugin({
minChunks: 2,
minSize: 0,
children: true,
deepChildren: true,
async: true
}),
...
]
似乎去掉 name
就可以了?这一点我还没搞清楚是为什么。
echarts 按需加载
import echarts from 'echarts/lib/echarts';
import 'echarts/lib/chart/map';
import 'echarts/lib/chart/pie';
import 'echarts/lib/chart/scatter';
import 'echarts/lib/chart/effectScatter';
import 'echarts/lib/component/geo';
import 'echarts/lib/component/tooltip';
import chinaJson from 'echarts/map/json/china.json';
...
echarts.registerMap('china', chinaJson);
...
需要注意,可以按需引入的模块列表见 https://github.com/ecomfe/ech...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。