通过打包配置提升单页web应用的首次加载速度

背景

前几天突然被拉去弄个紧急项目,然后就发现个现象,每次jenkins发版完后第一次打开都特别慢,如果在过去忙着实现功能可能都不会在意这种事情,多半就是接口之类响应慢吧,等等就好了。因为这次项目难度不大,很快就弄完了前端页面部分在等接口联调,闲着没事就开了个控制台:

巨大的bundle包

好嘛,一个2.4M的js文件加载了快1分钟,平时在内网开发、测试都还感觉不到,碰上这次超级春节长假大家都远程办公,问题就体现出来了。

分析解决

在前端技术快速发展的这么些年,工程化程度已经很高了,通常要开发一个前端项目都无法避免的需要用到各种第三方库,而最终的发布包为防止出现请求数过多的连接损耗(据说http2已经不怕了),会将所有js打包合并打包成一个bundle,同时根据一定的策略(比如说拆封第三方包,跟自有逻辑代码),又会将bundle切分成多个文件,称为chunk,所以这里的问题就是最终打出来的bundle包太大了,事实上安装项目的逻辑复杂度不应该有这么多依赖才对。

针对第三方包切分chunk

分析问题的第一步就是需要知道,到底是哪个第三方包贡献了最大体积,这里可以通过修改webpack的配置来实现,以vue-cli3的配置为例:

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
module.exports = {
...
// webpack配置
configureWebpack: {
entry: path.resolve(__dirname, 'src/main.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `chunk.${packageName.replace('@', '')}`;
},
},
},
},
},
},

...
}

之后再次执行打包就能将各第三方包切分到不同的chunk文件中,结果如下:

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
69
70
71
72
73
74
File                                      Size             Gzipped

dist\js\chunk.ant-design-vue.cd80903e.js 723.04 KiB 197.08 KiB
dist\js\chunk.element-ui.706b7bfd.js 651.31 KiB 159.23 KiB
dist\js\chunk.ant-design.5a37e430.js 487.75 KiB 149.15 KiB
dist\js\chunk.moment.64fc076a.js 265.93 KiB 70.29 KiB
dist\js\main.2ac5609b.js 93.14 KiB 11.44 KiB
dist\js\chunk.vue.fee2d0ce.js 64.44 KiB 23.14 KiB
dist\js\chunk.core-js.57eade5c.js 49.65 KiB 13.52 KiB
dist\js\chunk.lodash.cc36eec0.js 39.79 KiB 12.10 KiB
dist\js\chunk.vue-router.ff9d40d5.js 26.40 KiB 9.24 KiB
dist\js\chunk.tinycolor2.ca010ed2.js 14.40 KiB 5.08 KiB
dist\js\chunk.axios.104e3781.js 13.73 KiB 4.68 KiB
dist\js\chunk.dom-align.26879579.js 12.99 KiB 4.94 KiB
dist\js\chunk.async-validator.035de2e6.j 12.28 KiB 3.88 KiB
s
dist\js\chunk.vuex.6ce980ef.js 10.13 KiB 3.13 KiB
dist\js\chunk.resize-observer-polyfill.9 7.81 KiB 2.50 KiB
057ca08.js
dist\js\chunk.regenerator-runtime.1bdd7a 6.21 KiB 2.31 KiB
43.js
dist\js\chunk.dom-scroll-into-view.43888 6.06 KiB 2.41 KiB
cb2.js
dist\js\chunk.add-dom-event-listener.f73 4.25 KiB 1.64 KiB
30b55.js
dist\js\chunk.mutationobserver-shim.f3a2 3.56 KiB 1.52 KiB
719a.js
dist\js\chunk.normalize-wheel.968f2848.j 3.52 KiB 1.60 KiB
s
dist\js\chunk.babel-runtime.16b92121.js 3.19 KiB 1.01 KiB
dist\js\chunk.vuejs-logger.0f798d16.js 2.84 KiB 1.15 KiB
dist\js\chunk.enquire.js.1c0279d7.js 2.26 KiB 0.92 KiB
dist\js\chunk.setimmediate.8beabbc6.js 1.84 KiB 0.89 KiB
dist\js\chunk.process.e2ef65ec.js 1.73 KiB 0.73 KiB
dist\js\chunk.ismobilejs.606ec633.js 1.55 KiB 0.72 KiB
dist\runtime.7820c83ae420af7985bb.js 1.48 KiB 0.71 KiB
dist\js\chunk.component-classes.a9bdfe98 1.32 KiB 0.60 KiB
.js
dist\js\chunk.deepmerge.1a84198c.js 1.23 KiB 0.62 KiB
dist\js\chunk.babel.a29a890d.js 1.20 KiB 0.56 KiB
dist\js\chunk.timers-browserify.5700b26f 1.18 KiB 0.45 KiB
.js
dist\js\chunk.object-assign.693dae9a.js 1.08 KiB 0.61 KiB
dist\js\chunk.raf.950942e6.js 0.96 KiB 0.52 KiB
dist\js\chunk.vue-loader.42733d6e.js 0.85 KiB 0.48 KiB
dist\js\chunk.vue-ref.a8f4597e.js 0.70 KiB 0.36 KiB
dist\js\chunk.babel-helper-vue-jsx-merge 0.68 KiB 0.38 KiB
-props.d2b78d34.js
dist\js\chunk.classnames.9d5aa03e.js 0.67 KiB 0.43 KiB
dist\js\chunk.es6-object-assign.031da824 0.61 KiB 0.39 KiB
.js
dist\js\chunk.performance-now.1ef110c4.j 0.60 KiB 0.30 KiB
s
dist\js\chunk.throttle-debounce.9e006254 0.60 KiB 0.34 KiB
.js
dist\js\chunk.webpack.43202d1a.js 0.53 KiB 0.29 KiB
dist\js\chunk.json2mq.b3e258e0.js 0.52 KiB 0.33 KiB
dist\js\chunk.shallowequal.eb77517f.js 0.51 KiB 0.31 KiB
dist\js\chunk.dom-matches.b034f072.js 0.43 KiB 0.28 KiB
dist\js\chunk.array-tree-filter.f01d3ae1 0.40 KiB 0.26 KiB
.js
dist\js\chunk.shallow-equal.73a4cea0.js 0.27 KiB 0.20 KiB
dist\js\chunk.omit.js.44841e35.js 0.23 KiB 0.19 KiB
dist\js\chunk.dom-closest.0e518f19.js 0.22 KiB 0.18 KiB
dist\js\chunk.component-indexof.74394922 0.22 KiB 0.17 KiB
.js
dist\js\chunk.string-convert.d2e46b06.js 0.21 KiB 0.16 KiB
dist\js\chunk.is-negative-zero.0e1ef881. 0.17 KiB 0.15 KiB
js
dist\js\chunk.warning.6a7436eb.js 0.14 KiB 0.13 KiB
dist\css\npm.ant-design-vue.2f4f067f.c 403.26 KiB 50.80 KiB
ss
dist\css\npm.element-ui.8b09ba0f.css 225.79 KiB 34.43 KiB
dist\css\main.a59cd3b0.css 3.66 KiB 1.07 KiB

这样问题就很明显了,2个UI库antd跟element-ui合起来就超过了1.5M,平时因为偷懒对于这类UI库都是全包引入,即使对于antd就只使用了一个加载动画。

按需加载

一般来说对应的UI库都会提供按需加载方案,比如说andtd的,element-ui的,直接参考官网说明就好了,没有太多需要说明的,只是说为了方便管理,可以为不同UI组件库专门新建一个单独的js文件,将对于此UI库的组件引用都丢一块,然后在main.js中在引入这个文件就好了,比如:

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
import Vue from 'vue'

import {
Row,
Col,
Menu,
MenuItem,
Submenu,
Breadcrumb,
BreadcrumbItem,
Table,
TableColumn,
pagination,
Tabs,
TabPane,
Form,
FormItem,
Select,
Input,
Button,
Option,
Message,
Dialog,
Alert,
} from 'element-ui';

Vue.use(Row);
Vue.use(Col);
Vue.use(Menu);
Vue.use(MenuItem);
Vue.use(Submenu);
Vue.use(Breadcrumb);
Vue.use(BreadcrumbItem);
Vue.use(Table);
Vue.use(TableColumn);
Vue.use(pagination);
Vue.use(Tabs);
Vue.use(TabPane);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Select);
Vue.use(Input);
Vue.use(Button);
Vue.use(Option);
Vue.use(Dialog);
Vue.use(Alert);

Vue.prototype.$message = Message;

这样改造后的效果:

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
File                                      Size             Gzipped

dist\js\chunk.element-ui.6e23f077.js 257.04 KiB 61.32 KiB
dist\js\main.122644fe.js 89.45 KiB 10.15 KiB
dist\js\chunk.vue.d04d1a0b.js 64.44 KiB 23.14 KiB
dist\js\chunk.core-js.1ed5f8bb.js 49.65 KiB 13.52 KiB
dist\js\chunk.ant-design-vue.5e0dbd04. 31.19 KiB 10.20 KiB
js
dist\js\chunk.vue-router.7fb4eb4d.js 26.41 KiB 9.24 KiB
dist\js\chunk.axios.95604f08.js 13.73 KiB 4.68 KiB
dist\js\chunk.async-validator.4a2028fd 12.28 KiB 3.89 KiB
.js
dist\js\chunk.vuex.c302fad4.js 10.13 KiB 3.13 KiB
dist\js\chunk.resize-observer-polyfill 7.81 KiB 2.51 KiB
.27213368.js
dist\js\chunk.regenerator-runtime.1d99 6.21 KiB 2.32 KiB
9e29.js
dist\js\chunk.normalize-wheel.a8255151 3.52 KiB 1.60 KiB
.js
dist\js\chunk.lodash.e057cafe.js 3.24 KiB 1.34 KiB
dist\js\chunk.vuejs-logger.588a5a58.js 2.84 KiB 1.15 KiB
dist\js\chunk.babel-runtime.64168a82.j 2.68 KiB 0.87 KiB
s
dist\js\chunk.process.93970018.js 1.73 KiB 0.73 KiB
dist\runtime.45660f198238397a01ec.js 1.48 KiB 0.71 KiB
dist\js\chunk.deepmerge.cb65fd74.js 1.23 KiB 0.62 KiB
dist\js\chunk.babel.9ac1b3fd.js 1.20 KiB 0.56 KiB
dist\js\chunk.vue-loader.b9c5df7e.js 0.85 KiB 0.48 KiB
dist\js\chunk.vue-ref.2e5a80d7.js 0.70 KiB 0.36 KiB
dist\js\chunk.babel-helper-vue-jsx-mer 0.68 KiB 0.38 KiB
ge-props.8ffab4f7.js
dist\js\chunk.classnames.60b05cca.js 0.67 KiB 0.43 KiB
dist\js\chunk.es6-object-assign.aaeaaa 0.61 KiB 0.39 KiB
7f.js
dist\js\chunk.throttle-debounce.b38861 0.60 KiB 0.35 KiB
de.js
dist\js\chunk.webpack.0cb642d2.js 0.23 KiB 0.17 KiB
dist\css\chunk.element-ui.dbea684a.css 123.21 KiB 18.41 KiB
dist\css\chunk.ant-design-vue.0e251fa4 31.62 KiB 4.43 KiB
.css
dist\css\main.5a138976.css 3.77 KiB 1.10 KiB

可以说是效果相当明显了,antd直接就给压缩到了32k,于是就可以把分包策略再改一改了,把比较可能产生变化的element-ui单独拿出来作为一个chunk,其他第三方库作为一个统一的chunk,这样可以提供非首次加载用户的本地缓存命中率,只需要改动webpack配置的name方法逻辑

1
2
3
4
5
6
7
8
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
if ( packageName.indexOf("element-ui") != -1 ) {
return 'chunk.element-ui';
} else {
return 'chunk.vendors';
}
},

修改后打包得到的文件列表

1
2
3
4
5
6
7
8
9
File                                      Size             Gzipped

dist\js\chunk.element-ui.6e23f077.js 257.04 KiB 61.32 KiB
dist\js\chunk.vendors.a0611066.js 240.81 KiB 81.35 KiB
dist\js\main.a9029da6.js 89.00 KiB 9.97 KiB
dist\runtime.18ae6b183bfbfbed66d9.js 1.48 KiB 0.71 KiB
dist\css\chunk.element-ui.dbea684a.css 123.21 KiB 18.41 KiB
dist\css\chunk.vendors.9d453f4b.css 31.62 KiB 4.43 KiB
dist\css\main.5a138976.css 3.77 KiB 1.10 KiB

多入口配置

基于此,想起来之前另一个react工程也有类似的问题,也依葫芦画瓢分析一把,react的webpack配置需要安装对应的依赖包react-app-rewired,然后修改config-overrides.js文件

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
module.exports = function override(config) {

...

config.optimization = {
...config.optimization,
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];

return `chunk.${packageName.replace('@', '')}`;
},
},
},
},
};

...
};

然后得到分包的输出结果

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
File sizes after gzip:

84.27 KB build\static\js\chunk.ol.6c49b241.chunk.js
69.51 KB build\static\js\chunk.codemirror.cde7c54e.chunk.js
51.3 KB build\static\js\chunk.material-ui.88079b29.chunk.js
39.62 KB build\static\js\chunk.react-dom.0be5f8db.chunk.js
38.16 KB build\static\js\chunk.ra-core.d9b7911d.chunk.js
29.95 KB build\static\js\chunk.ra-ui-materialui.ba718a51.chunk.js
23.05 KB build\static\js\chunk.react-admin-color-input.e88da086.chunk.js
22.56 KB build\static\js\chunk.qiniu-js.a4c1cdcf.chunk.js
16.01 KB build\static\js\chunk.react-color.f87b4091.chunk.js
12.53 KB build\static\js\chunk.pretty-format.e226feec.chunk.js
11.63 KB build\static\js\chunk.easymde.2c894284.chunk.js
10.01 KB build\static\js\chunk.lodash.aa8cc043.chunk.js
9.32 KB (-2.87 KB) build\static\js\main.723d3f0e.chunk.js
7.85 KB build\static\js\chunk.marked.9561a2a9.chunk.js
7.21 KB build\static\js\chunk.popper.js.57d79d17.chunk.js
6.65 KB build\static\js\chunk.aria-query.5ee48286.chunk.js
6.2 KB build\static\js\chunk.testing-library.f707b4d2.chunk.js
6.11 KB build\static\js\chunk.redux-saga.4f05fdc3.chunk.js
5.76 KB build\static\js\chunk.jss.7dfcb01a.chunk.js
5.67 KB build\static\js\chunk.buffer.400cae46.chunk.js
5.56 KB build\static\js\chunk.openlayers.b9ba0e3f.chunk.js
5.02 KB build\static\js\chunk.tinycolor2.0152cc0d.chunk.js
4.9 KB build\static\js\chunk.final-form.a2ed567f.chunk.js
4.06 KB build\static\js\chunk.es-abstract.03d84b43.chunk.js
3.86 KB build\static\js\chunk.react-redux.e044a5d4.chunk.js
3.68 KB build\static\js\main~wechat.79f40213.01cf27ee.chunk.js
3.18 KB build\static\js\chunk.inflection.eb2c32e8.chunk.js
3.13 KB build\static\js\chunk.react-dropzone.5762be11.chunk.js
2.89 KB build\static\js\chunk.history.b20e3aee.chunk.js
2.85 KB build\static\js\chunk.react-final-form.e60b6236.chunk.js
2.85 KB build\static\js\chunk.typo-js.95619217.chunk.js
2.69 KB build\static\js\chunk.jsonexport.ff93d053.chunk.js
2.6 KB build\static\css\chunk.easymde.e507eba4.chunk.css
2.59 KB build\static\js\chunk.connected-react-router.91025c09.chunk.js
2.55 KB build\static\js\chunk.react.233452ee.chunk.js
2.36 KB build\static\js\chunk.rbush.90c04ace.chunk.js
2.31 KB build\static\js\chunk.redux.fbae5c97.chunk.js
2.3 KB build\static\js\chunk.regenerator-runtime.aaeb1a4e.chunk.js
2.08 KB build\static\js\chunk.react-transition-group.7ee1fd23.chunk.js
1.95 KB build\static\js\chunk.material-colors.557115a5.chunk.js
1.91 KB build\static\js\chunk.scheduler.0dec7080.chunk.js
1.86 KB build\static\js\chunk.react-simplemde-editor.a90621b2.chunk.js
1.84 KB build\static\js\chunk.react-router.829aa729.chunk.js
1.83 KB build\static\js\chunk.css-vendor.eb5a54d1.chunk.js
1.75 KB build\static\js\chunk.reactcss.d9b3bf2f.chunk.js
1.75 KB build\static\js\chunk.lodash.get.dd9db41b.chunk.js
1.58 KB build\static\js\chunk.ra-language-chinese.10c431b8.chunk.js
1.52 KB build\static\js\chunk.sheerun.a70752b9.chunk.js
1.5 KB build\static\js\chunk.path-to-regexp.fc22d238.chunk.js
1.5 KB build\static\js\chunk.node-polyglot.2f122d25.chunk.js
1.47 KB build\static\js\chunk.query-string.04ad72fd.chunk.js
1.46 KB build\static\js\chunk.ra-language-english.67254965.chunk.js
1.41 KB build\static\js\chunk.babel-preset-react-app.d30faa3e.chunk.js
1.4 KB build\static\js\chunk.final-form-arrays.24364da7.chunk.js
1.39 KB build\static\js\chunk.recompose.5a045c3f.chunk.js
1.2 KB build\static\js\chunk.loglevel.933b7b7e.chunk.js
1.19 KB build\static\js\chunk.react-router-dom.dab4e8f1.chunk.js
1.18 KB build\static\js\chunk.file-selector.e9531dac.chunk.js
1.18 KB build\static\js\chunk.babel.39089000.chunk.js
1.08 KB build\static\js\chunk.date-fns.3ddb3bc5.chunk.js
1.06 KB build\static\js\chunk.eventemitter3.5c2090de.chunk.js
983 B build\static\js\chunk.object-keys.48b9c6ce.chunk.js
973 B build\static\js\chunk.jss-plugin-default-unit.0195203b.chunk.js
926 B build\static\js\chunk.tslib.7e9561da.chunk.js
824 B build\static\js\chunk.mini-create-react-context.da644b0f.chunk.js
806 B build\static\js\chunk.reselect.3ce0305f.chunk.js
805 B build\static\js\chunk.base64-js.77bf602a.chunk.js
804 B build\static\js\chunk.react-is.cf746209.chunk.js
770 B build\static\js\chunk.codemirror-spell-checker.fb2d4fc7.chunk.js
761 B build\static\js\chunk.jss-plugin-global.1a65138f.chunk.js
756 B build\static\js\chunk.process.5ab7aff6.chunk.js
746 B build\static\js\chunk.react-admin.764cfd97.chunk.js
746 B build\static\js\runtime.1e297267.js
614 B build\static\js\chunk.icons.89777078.chunk.js
586 B build\static\js\chunk.jss-plugin-nested.c6bc094c.chunk.js
586 B build\static\js\chunk.object-assign.9b6ef571.chunk.js
569 B build\static\js\chunk.decode-uri-component.8f3861ab.chunk.js
561 B build\static\js\chunk.ieee754.be4eac64.chunk.js
553 B build\static\js\chunk.hoist-non-react-statics.ad71935f.chunk.js
538 B build\static\js\chunk.ra-i18n-polyglot.4ad09dd1.chunk.js
529 B build\static\js\chunk.prop-types.37adbc95.chunk.js
518 B (-3.43 KB) build\static\js\wechat.79f40213.3e23a8d8.chunk.js
508 B build\static\js\chunk.normalize-scroll-left.c18007b5.chunk.js
508 B build\static\js\chunk.attr-accept.4f82d5c7.chunk.js
477 B build\static\js\chunk.define-properties.5d0b265d.chunk.js
477 B build\static\js\chunk.has-symbols.58c64948.chunk.js
473 B build\static\js\chunk.string.prototype.trim.60ccf6ca.chunk.js
471 B build\static\js\chunk.function-bind.73a01976.chunk.js
452 B build\static\js\chunk.wait-for-expect.342f7cf0.chunk.js
446 B build\static\js\chunk.resolve-pathname.1a35cae1.chunk.js
427 B build\static\js\chunk.es-to-primitive.5a3e4a30.chunk.js
390 B build\static\js\chunk.jss-plugin-vendor-prefixer.90e53b70.chunk.js
368 B build\static\js\chunk.jss-plugin-rule-value-function.dbd7eeb2.chunk.js
367 B build\static\js\chunk.change-emitter.910274e1.chunk.js
365 B build\static\js\chunk.webpack.02d08169.chunk.js
364 B build\static\js\chunk.for-each.42258c7b.chunk.js
358 B build\static\js\chunk.classnames.0e67c5d0.chunk.js
348 B build\static\js\chunk.jss-plugin-camel-case.2b21a29d.chunk.js
346 B build\static\js\chunk.is-callable.75d4870b.chunk.js
335 B build\static\js\chunk.invariant.4334bd5b.chunk.js
321 B build\static\js\chunk.value-equal.f6392b61.chunk.js
315 B build\static\js\chunk.fbjs.3ef0c11d.chunk.js
310 B build\static\js\chunk.symbol-observable.858c5c03.chunk.js
289 B build\static\js\chunk.clsx.c7728ae6.chunk.js
268 B build\static\js\chunk.is-in-browser.f57fcd0e.chunk.js
266 B build\static\js\chunk.split-on-first.5b8d94bd.chunk.js
256 B build\static\js\chunk.jss-plugin-props-sort.897b6dc3.chunk.js
232 B build\static\js\chunk.hyphenate-style-name.20142260.chunk.js
218 B build\static\js\chunk.tiny-warning.7ffaad56.chunk.js
218 B build\static\js\chunk.strict-uri-encode.d55fcdde.chunk.js
194 B build\static\js\chunk.gud.8f3c4f90.chunk.js
186 B build\static\js\chunk.tiny-invariant.1c87f7a1.chunk.js
178 B build\static\js\chunk.has.501bd54b.chunk.js
171 B build\static\js\chunk.isarray.179afdd1.chunk.js
143 B build\static\js\chunk.warning.4588dba4.chunk.js
104 B build\static\css\chunk.react-admin-color-input.198dddac.chunk.css

这个工程同时服务于PC端跟微信公众号,而在微信公众号上其实只有2个简单的页面,并不需要用到这么多的依赖,所以首先的优化点就是将微信的入口(entry)单独拆分出来,需要用到新的开发依赖react-app-rewire-multiple-entry,然后修改配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const multipleEntry = require('react-app-rewire-multiple-entry')([
{
entry: 'src/entry/wechat.tsx',
template: 'public/wechat.html',
outPath: '/wechat.html'
},
]);

module.exports = function override(config) {

...
multipleEntry.addMultiEntry(config);
...

}

最终打包得到的文件如下,可以发现多了很多数字开头的chunk,webpack会自动分析不同入口的依赖以对chunk进行切分

1
2
3
4
5
6
7
8
9
File sizes after gzip:

434.59 KB build\static\js\5.e9065b4b.chunk.js
117.82 KB build\static\js\0.c845b199.chunk.js
12.19 KB (+2.87 KB) build\static\js\main.d4ac2a7d.chunk.js
3.94 KB (+3.43 KB) build\static\js\wechat.79f40213.63b7857a.chunk.js
2.63 KB build\static\css\5.01a907c6.chunk.css
745 B build\static\js\runtime-main.5e6b2859.js
745 B build\static\js\runtime-wechat.79f40213.1c8b1700.js

组件懒加载

通过入口的切分,微信端的问题是解决了,可PC端的问题依然存在,其中一个体积靠前的包是codemirror,一个富编辑器组件,但其实用到的只有2个页面,绝大部分页面并不需要用上,可是即使进行手工指定的chunk切分,也依然会在进入第一个页面的时候就进行加载,所以这里就需要用到react v16.6的新特性懒加载了

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
import React, { lazy, Suspense } from 'react';

const MarkdownInput = lazy(() => import("@/components/MarkdownInput"));

class WordEdit extends React.Component<any, any> {

...
fallback = () => {
return (
<div>Loading...</div>
);
}

render() {
return (
...
<Suspense fallback={this.fallback()}>
<MarkdownInput
source="content"
label=""
validate={[required()]}
/>
</Suspense>
...
);
}
...
}

通过lazy-suspense的组合,就能够实现只有在当前页面被渲染时,才会第一次对依赖文件进行加载

1
2
3
4
5
6
7
8
9
10
11
12
File sizes after gzip:

341.03 KB build\static\js\7.683cff2d.chunk.js
117.81 KB build\static\js\0.b6431874.chunk.js
92.71 KB build\static\js\1.b33bd2fd.chunk.js
11.99 KB (+2.68 KB) build\static\js\main.474f9f9f.chunk.js
3.93 KB (+3.43 KB) build\static\js\wechat.79f40213.99dfb5a8.chunk.js
2.6 KB build\static\css\1.e507eba4.chunk.css
1.46 KB build\static\js\runtime-main.beea653e.js
744 B build\static\js\runtime-wechat.79f40213.f5287610.js
598 B build\static\js\2.3a0a229a.chunk.js
104 B build\static\css\7.198dddac.chunk.css

以上是将组件修改为懒加载后的分包情况,可以发现,又多出了一些chunk

import()动态引入

最后要解决另一个体积比较大的包ol,这是一个地图工具包,本身不像UI组件库提供了按需引入的方式,也不像一个react的组件可以进行按需引入,所以就涉及到动态引入的概念了,这里是使用一个静态类对ol做了一层封装,最后改造如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const MapEdit = (props: any) => {

...
useEffect(() => {

...
// 所有需要用到地图工具类的地方都用import()包装一层
import("@/utils/MapHelper").then((MapHelper: any) => {
...
MapHelper.default.xxx();
...
}
...
}, [map]);
}

针对所有可以懒加载、动态引入的代码进行处理后再来看看打包情况

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
File sizes after gzip:

118.11 KB build\static\js\17.a9eb382a.chunk.js
100.62 KB build\static\js\18.1d829a41.chunk.js
95.06 KB build\static\js\8.d070d1df.chunk.js
93.94 KB build\static\js\16.640ff7f2.chunk.js
93.11 KB (-149.37 KB) build\static\js\9.61e74371.chunk.js
91.56 KB build\static\js\5.c3148b97.chunk.js
22.57 KB build\static\js\42.f2fefa65.chunk.js
10.5 KB (+9.92 KB) build\static\js\4.b559bdcd.chunk.js
8.59 KB (+6.18 KB) build\static\js\1.61841ee3.chunk.js
7.63 KB (-84.94 KB) build\static\js\3.908c3785.chunk.js
7.11 KB (-2.7 KB) build\static\js\main.71c915fc.chunk.js
6.1 KB build\static\js\19.de1d6360.chunk.js
5.72 KB (-90.3 KB) build\static\js\2.f96bb738.chunk.js
5.41 KB build\static\js\7.a411e74e.chunk.js
5.08 KB build\static\js\20.a71d9383.chunk.js
4.29 KB (-87.52 KB) build\static\js\0.9cead90e.chunk.js
3.94 KB (-8 B) build\static\js\wechat.79f40213.1a92a171.chunk.js
3.86 KB build\static\js\21.7614d60e.chunk.js
3.42 KB (+597 B) build\static\js\10.f51ec5a7.chunk.js
3.12 KB build\static\js\25.a7422a63.chunk.js
2.6 KB (+2.5 KB) build\static\css\9.e507eba4.chunk.css
2.42 KB build\static\js\6.55f3ef2b.chunk.js
2.38 KB build\static\js\22.49a7800f.chunk.js
1.96 KB build\static\js\24.6af9414c.chunk.js
1.87 KB (+340 B) build\static\js\runtime-main.9ed5ec8a.js
1.55 KB build\static\js\23.866bbcf4.chunk.js
1.14 KB build\static\js\runtime-wechat.79f40213.39bfa3d0.js
715 B build\static\js\27.50be8cd5.chunk.js
604 B (+194 B) build\static\js\11.787754a0.chunk.js
482 B build\static\js\41.34b0df64.chunk.js
475 B build\static\js\26.8e4df89e.chunk.js
439 B build\static\js\32.3e8eafab.chunk.js
439 B build\static\js\31.918c0125.chunk.js
434 B build\static\js\40.827efc5b.chunk.js
429 B build\static\js\30.9389c44c.chunk.js
423 B build\static\js\36.0cfca1fa.chunk.js
422 B build\static\js\29.477699f4.chunk.js
412 B build\static\js\28.309fbb6b.chunk.js
391 B build\static\js\33.ba9c1dbe.chunk.js
355 B build\static\js\38.9728699e.chunk.js
344 B build\static\js\37.c3aec284.chunk.js
325 B build\static\js\34.d52c54fc.chunk.js
276 B build\static\js\39.ec6d94f0.chunk.js
265 B build\static\js\35.5fe1ff75.chunk.js
104 B build\static\css\18.198dddac.chunk.css
104 B build\static\css\17.198dddac.chunk.css

一下就多出来好几十个文件,来看看最终的加载效果,直接干掉了一个3M多的大包,虽然受制于带宽还是稍显有点儿慢,但比起过去等上1-2分钟已经是质的飞跃了。
最终效果