用 react antd开发了一个页面,最后webpack打包的大小竟然达到了 1.9M ,gzip压缩之后也有500kb。
这超出了能承受的范围,我一个小网站哪有这么大的带宽。
1. 找原因
开始的时候并不知道是antd的锅,后来发现了一些工具可以提供UI来分析打包的js的组成部分。比如这个: https://www.npmjs.com/package/webpack-bundle-analyzer
借助这个工具,我看到了打包的js绝大部分都是antd带来的。我首先确认了我配置了antd官方提供按需加载的,所以应该不是按需加载出了问题。然后经过仔细分析发现主要是下面4个原因:
icons
antd的icons占用了很大的部分。原因是正常情况下icons是不会按需加载的,只能全部引用。很多人也遇到这个问题,antd官方给出了一个workaround,稍微有点麻烦,因为你需要自己去找你引用了那些icon:https://github.com/ant-design/ant-design/issues/12011#issuecomment-420038579
base css
我的页面基本上只用了一个table,但是antd的css体积也达到了了几百kb。原因是antd的css虽然可以按需加载,但是一些基础的base css是一定会被打包的。这一点没有找到解决方案。
moment.js
我自己没有用到moment.js,但是antd用到了moment.js,moment.js也是占用了不小的体积。这个是有一个plugin可以减少moment.js的体积的。原理是让webpack只加载你用到了语言包。插件是:new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/)
lodash
同样我没有用到lodash,但是antd用到了。lodash也有一个plugin去减少webpack打包的体积:lodash-webpack-plugin
2. 使用antd的公共cdn去解决。
上面分析了antd之所以很大的原因。有些是解决不了的,比如css太大的问题。那么自然就想到了能不能不把antd打包进去,而是在网页中通过cdn引入antd呢?cdn又不走我的网站带宽。答案是可以的,这就是webpack的externals配置。于是我决定使用antd的cdn去解决打包体积太大的问题。
3. 解决 antd is not defined 问题。
开始的时候这样做的,在webpack的externals里面只配置了antd
externals: { 'antd': 'antd' },
然后在页面引入antd的cdn
<link rel="stylesheet" href="https://cdn.bootcss.com/antd/3.23.3/antd.min.css"> <script src="https://cdn.bootcss.com/antd/3.23.3/antd.min.js" type="text/javascript"></script>
这样做之后,整个打包的体积直接降到了几百kb,不过遗憾的是打开页面出现了 antd is not defined。这个开始也是束手无策,网上并没有搜到好的解决办法。后来找到了原因,不能直接引入antd的cdn,在这之前要把antd依赖的其他资源也加进来。那么antd依赖的哪些呢?首先react和react-dom是必须的。加进来之后,还是有问题。于是再仔细读antd的文档,发现下面的内容:
支持环境# 现代浏览器和 IE9 及以上(需要 polyfills)。
强烈不推荐使用已构建文件,这样无法按需加载,而且难以获得底层依赖模块的 bug 快速修复支持。 注意:3.0 之后引入 antd.js 前你需要自行引入 moment。
吐槽下这个推荐。虽然它强烈不推荐使用已构建文件,但是没办法啊,antd的按需加载做的确实有问题。
因为我用的3.x 版本,所以还要引入polyfills和moment。加上之后,终于可以了。同时因为webpack去掉了polyfills,react,react-dom,最终的打包体积只剩下了100kb了(gzip压缩前)。
最终的代码:
externals: { 'react': 'React', 'react-dom': 'ReactDOM', 'antd': 'antd' },
<link rel="stylesheet" href="https://cdn.bootcss.com/antd/3.23.3/antd.min.css"> <script src="https://cdn.bootcss.com/babel-polyfill/7.6.0/polyfill.min.js"></script> <script src="https://cdn.bootcss.com/react/16.9.0/umd/react.production.min.js"></script> <script src="https://cdn.bootcss.com/react-dom/16.9.0/umd/react-dom.production.min.js"></script> <script src="https://cdn.bootcss.com/moment.js/2.24.0/moment.min.js"></script> <script src="https://cdn.bootcss.com/moment.js/2.24.0/locale/zh-cn.js"></script> <script src="https://cdn.bootcss.com/antd/3.23.3/antd.min.js" type="text/javascript"></script>
最后温馨提示,用cdn最好加上fallback,当cdn不幸挂了的话,换另一家cdn。
<script>window.antd||document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/3.23.6/antd.min.js"><\/script>')</script>