umi项目本地构建优化
# webpack层面优化[要点]
- 详见【vue-config详解及实践】
# 配置
# externals
可处理包大小及构建速度;
const getArgvOptions = require('../argvOptions');
const { join } = require('path');
const argvOptions = getArgvOptions();
const base = argvOptions.base || '/';
const ROUTER_BASE = base.lastIndexOf('/') === base.length - 1 ? base : `${base}/`;
module.exports = (api, options = {}) => {
api.chainWebpackConfig(config => {
config.externals({
react: 'window.React',
'react-dom': 'window.ReactDOM',
'react-router': 'window.ReactRouter',
'react-router-dom': 'window.ReactRouterDOM',
dva: 'window.dva',
'umi-plugin-locale': 'window.UmiPluginLocale',
'umi-plugin-locale/lib/locale': 'window.UmiPluginLocale', // 兼容高版本umi国际化
'umi/locale': 'window.UmiLocale',
'umi-plugin-react/locale': 'window.UmiLocale',
moment: 'window.moment',
jquery: 'window.jQuery',
'@antv/data-set': 'window.DataSet',
antd: 'window.antd',
// '@ant-design/icons': 'window.cloud.ant-design-icons',
bizcharts: 'window.BizCharts',
// 'ace-builds': 'window.cloud.ace-builds',
'lottie-web': 'window.lottie',
// jsplumb: 'window.cloud.jsPlumb',
lodash: 'window._',
});
});
api.addHTMLHeadScript(() => {
const scripts = (options.scripts || []).map(sub => {
return { src: sub };
});
return [
{ src: `${ROUTER_BASE}js/react.production.min.js` },
{ src: `${ROUTER_BASE}js/react-dom.production.min.js` },
{ src: `${ROUTER_BASE}js/react-router.min.js` },
{ src: `${ROUTER_BASE}js/react-router-dom.min.js` },
{ src: `${ROUTER_BASE}js/dva.min.js` },
{ src: `${ROUTER_BASE}js/umi-plugin-locale.js` },
{ src: `${ROUTER_BASE}js/moment.min.js` },
{ src: `${ROUTER_BASE}js/moment/zh-cn.js` },
{ src: `${ROUTER_BASE}js/moment/zh-tw.js` },
{ src: `${ROUTER_BASE}js/antd.min.js` },
{ src: `${ROUTER_BASE}js/antd-with-locales.min.js` },
{ src: `${ROUTER_BASE}js/jquery.min.js` },
{ src: `${ROUTER_BASE}js/lodash.min.js` },
{ src: `${ROUTER_BASE}js/biz-charts.min.js` },
{ src: `${ROUTER_BASE}js/lottie.min.js` },
...scripts,
];
});
api.addHTMLLink(() => {
return (options.stylesheets || []).map(sub => {
return { href: sub, rel: 'stylesheet' };
});
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# moment
ignoreMomentLocale: true, // 忽略 moment 的 locale 文件,用于减少尺寸 ===> 使用 dayjs 代替了
1
// 过滤掉moment的那些不使用的国际化文件,过滤后打包出来的vendors.xxx.async.js可以减少290KB
config.plugin("replace").use(require("webpack").ContextReplacementPlugin).tap(() => {
return [/moment[/\\]locale$/, /zh-cn|en-us/];
});
1
2
3
4
2
3
4
# splitChunks
chainWebpack(config) {
config.optimization.splitChunks({
chunks: "all", //async异步代码分割 initial同步代码分割 all同步异步分割都开启
automaticNameDelimiter: "~",
name: true,
minSize: 30000, //字节 引入的文件大于30kb才进行分割
//maxSize: 50000, //50kb,尝试将大于50kb的文件拆分成n个50kb的文件
minChunks: 1, //模块至少使用次数
// maxAsyncRequests: 5, //同时加载的模块数量最多是5个,只分割出同时引入的前5个文件
// maxInitialRequests: 3, //首页加载的时候引入的文件最多3个
// name: true, //缓存组里面的filename生效,覆盖默认命名
cacheGroups: {
react: {
name: "react",
test: /[\\/]node_modules[\\/](react)[\\/]/,
priority: -9,
enforce: true,
},
reactDom: {
name: "react-dom",
test: /[\\/]node_modules[\\/](react-dom)[\\/]/,
priority: -9,
enforce: true,
},
echarts: {
name: "echarts",
test: /[\\/]node_modules[\\/](echarts)[\\/]/,
priority: -9,
enforce: true,
},
antd: {
name: "antd",
test: /[\\/]node_modules[\\/](@ant-design|antd|antd-mobile)[\\/]/,
priority: -10,
enforce: true,
},
vendors: {
name: "vendors",
test: /[\\/]node_modules[\\/]/,
priority: -11,
enforce: true,
},
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
其他方式:
config.optimization.splitChunks({
chunks: 'all',
automaticNameDelimiter: '.',
name: true,
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 10,
maxInitialRequests: 5,
cacheGroups: {
vendors: {
name: 'vendors',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|lodash|lodash-decorators|redux-saga|re-select|dva|moment)[\\/]/,
priority: -10,
},
antdesigns: {
name: 'antdesigns',
chunks: 'all',
test: /[\\/]node_modules[\\/](@ant-design|antd)[\\/]/,
priority: -11,
},
default: {
minChunks: 1,
priority: -20,
reuseExistingChunk: true
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 引入插件
# happypack
Happypack 的作用就是将文件解析任务分解成多个子进程并发执行。子进程处理完任务后再将结果发送给主进程。利用 js 的多进程来实现打包加速。
const HappyPack = require('happypack');
config.plugin('HappyPack').use(HappyPack, [{
id: 'js',
loaders: ['babel-loader'],
threadPool: HappyPack.ThreadPool({ size: require('os').cpus().length }),
}])
1
2
3
4
5
6
2
3
4
5
6
注意:Ahmad Amireh 推荐使用 thread-loader,并宣布将不再继续维护 happypack,所以不推荐使用它
# uglifyjs
不支持es6压缩,推荐用terser;
chainWebpack(config) {
// 删除 打包时 进度条插件
// config.plugins.delete('progress');
config.merge({
plugin: {
install: {
plugin: require('uglifyjs-webpack-plugin'),
args: [{
sourceMap: false,
uglifyOptions: {
compress: {
drop_console: true, // 删除所有的 `console` 语句
},
output: {
beautify: false,// 最紧凑的输出
comments: false,// 删除所有的注释
},
}
}]
}
},
optimization: {
minimize: true,
splitChunks: {
chunks: 'async',
minSize: 20000, //生成块的最小大小(以字节为单位)1024字节=1KB。
minChunks: 1, //拆分前必须共享模块的最小块数。
maxInitialRequests: 30, //入口点的最大并行请求数。
automaticNameDelimiter: '.',
cacheGroups: {
vendor: {
name: 'vendors',
test: /^.*node_modules[\\/](?!ag-grid-|lodash|wangeditor|react-virtualized|rc-select|rc-drawer|rc-time-picker|rc-tree|rc-table|rc-calendar|echarts|echarts-gl|xlsx|@ant-design|antd|ali-oss).*$/,
priority: -10,
enforce: true,
},
}
}
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# terser-webpack-plugin
如果你使用的是 webpack v5 或以上版本,你不需要安装这个插件。webpack v5 自带最新的 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本。
npm install --save-dev terser-webpack-plugin@4
1
const TerserPlugin = require('terser-webpack-plugin');
config.plugin('TerserPlugin').use(TerserPlugin, [{
parallel: require('os').cpus().length - 1,
terserOptions: {
compress: {
inline: false
},
mangle: {
safari10: true
}
}
}])
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# hard-source-webpack-plugin
在 umi2 默认使用了 hard-source-webpack-plugin
插件提升构建速度,不知道为啥在 umi3 干掉了这一功能
第一步肯定是安装插件了
yarn add --dev hard-source-webpack-plugin
1
第二步在 config.ts
中使用 chainWebpack
配置插件
import { defineConfig } from 'umi';
export default defineConfig({
// webpack 配置
chainWebpack: function (config, { webpack }) {
// 打包加速
config.plugin('hardSource').use(HardSourceWebpackPlugin);
// 对下面配置的 module 不进行缓存
config.plugin('hardSourceExcludeModule').use(HardSourceWebpackPlugin.ExcludeModulePlugin, [
[
{
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/,
},
{
test: /eslint-loader/,
},
{
test: /.*\.DS_Store/,
},
],
]);
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# compression-webpack-plugin
项目安装plugin依赖
yarn add compression-webpack-plugin -D
1
在config/config.js目录下进行配置
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const prodGzipList = ['js', 'css'];
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') { // 生产模式开启
config.plugin('compression-webpack-plugin').use(
new CompressionWebpackPlugin({
// filename: 文件名称,这里我们不设置,让它保持和未压缩的文件同一个名称
algorithm: 'gzip', // 指定生成gzip格式
test: new RegExp('\\.(' + prodGzipList.join('|') + ')$'), // 匹配哪些格式文件需要压缩
threshold: 10240, //对超过10k的数据进行压缩
minRatio: 0.6 // 压缩比例,值为0 ~ 1
})
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 最后设置
plugin.config.js
const HappyPack = require('happypack');
const TerserPlugin = require('terser-webpack-plugin');
export default config => {
config.plugin('HappyPack').use(HappyPack, [{
id: 'js',
loaders: ['babel-loader'],
threadPool: HappyPack.ThreadPool({ size: require('os').cpus().length }),
}])
config.plugin('TerserPlugin').use(TerserPlugin, [{
parallel: require('os').cpus().length - 1,
terserOptions: {
compress: {
inline: false
},
mangle: {
safari10: true
}
}
}])
// 设置 alias
config.resolve.alias.set('@', path.join(__dirname, '../src/'));
config.resolve.alias.set('@node_modules', path.join(__dirname, '../node_modules/'));
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
上次更新: 2022/04/15, 05:41:29