用 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>