react项目优化之webpack
<p>开门见山,由于我们项目的前端只有一个 bundle ,所有代码都在一个js文件里,随着功能不断的堆叠,体积已经到无法忍受的地步了(gzip后即将突破300k),导致首屏的时间不停的涨啊涨,最近一周富裕了一点人力赶紧做一次优化,暂时缓住了势头。</p> <p>react+webapck的优化文章现在一搜一大把,这次只说说我的技术方案,以及我是如何分析和优化的。</p> <h2>技术方案</h2> <p>首先,我先说下, gzip , cdn 等等前端优化手段我们都是有的,这次主要是为了解决 bundle 过大的问题。</p> <p>我们项目的总体架构很简单易懂, react 全家桶: react + redux ,哈哈。共有三个系统,每个系统有自己单独的工程。由于是 Hybrid 应用,为了尽可能贴合 APP 内的切页效果,每个工程又都是采用的是单页+多页的形式。</p> <p>由于系统多+单页多页混合,导致我们的路由比较混乱,本来打算上 react-router 进行一次大的改版,但是时间不够充裕,综合时间,开发成本角度来看,我定了一个简单并可以快速实施的方案:</p> <ol> <li>抽取出三个工程的公共代码打包出一个 bundle 。比如 react , redux , redux-thunk 以及一些 polyfill ,因为这些都是长时间不会变动的代码,所以可以最大程度的命中缓存,并且每次发布不需要重新下载这部分代码,在项目中就用 externals 的方案去引用这些代码。</li> <li>code split ,每个工程按照多页的路由做代码切割,每个多页路由都有自己的 bundle 。</li> <li>异步加载,每个多页路由都会对应各自的辅助页面和组件代码,都使用异步加载的方式。</li> </ol> <h2>如何优化bundle体积</h2> <h2>code split+异步加载</h2> <p>这个很简单,使用 webpack 的 requre.ensure 就可以了</p> <pre> <code class="language-javascript">addPage(cb){ require.ensure([], function () { cb({ Page1: require('../../pages/Page1'), Page2: require('../../pages/Page2') }) }); } </code></pre> <p>这里 Page1 和 Page2 的代码都是异步加载的,具体的部分涉及到路由的设计,我们目前的方案非常的简单和随便,就不细说了。我也看了 react-router 的解决方案,觉得写起来复杂了一些,后续可能会在寻找更好的方案或者自己撸一个。</p> <p>这里需要注意,虽然我们的 js 代码做了拆分, css 文件还是希望打包成一个,所以需要给 ExtractTextPlugin 增加 allChunks 的配置。</p> <pre> <code class="language-javascript">new options.ExtractTextPlugin('[name]@[contenthash].css', {allChunks: true}) </code></pre> <h2>externals</h2> <p>我将 react , redux 等等公用的代码打包成一个 lib.js ,然后暴露在全局变量上。每个工程里都会先去引用这个 lib.js ,在 webpack 的配置里就只需要配置上 externals 就可以了</p> <h2>其他插件</h2> <p>我们还用了一些其他插件来尽可能的优化体积</p> <ul> <li>UglifyJsPlugin 不多说了,代码混淆压缩</li> <li>DedupePlugin 消除重复引用的模块,好像 webpack2 已内置,使用 webpack2 的可以忽略</li> <li>OccurrenceOrderPlugin 让依赖次数多的模块靠前分到更小的id来达到输出更多的代码</li> <li>CommonsChunkPlugin 这个我们没有用,但是大家可以去看看,对于多页应用它可以帮助你将一些公共代码打包</li> </ul> <h2>如何分析bundle过大的问题</h2> <p>在做代码拆分的时候我曾遇到过一个问题,如何确认我的代码已经是最优的了,如何确认我无法继续优化了?这就需要我们去查看,这个 bundle 究竟打包了哪些代码,是否这些都是我们需要的。</p> <p>首先我推荐一个 网址 ,这里介绍了很多 webpack 优化的工具。</p> <p>我自己推荐两个 bundle 体积的可视化分析工具</p> <ol> <li><a href="/misc/goto?guid=4959741211444866052" rel="nofollow,noindex">webpack-visualizer</a></li> <li><a href="/misc/goto?guid=4959741211529732867" rel="nofollow,noindex">webpack-bundle-analyzer</a></li> </ol> <p>具体如何使用我就不介绍了,它们的文档写的很清楚大家可以去文档上看,他们都可以很清楚的看到每个 bundle 分别打包了哪些代码,哪些占据了最大的体积,也可以观察哪些代码其实是无用的可以优化掉的。</p> <p>这里我还遇到过一个问题,比如我在 detail.chunk.js 里发现引入了一个 loading 组件,但是我映象里详情页并没有引入 loading 组件呀,这时候就需要去寻找 loading 是被谁依赖了。之前我都是用webstorm的 find usages 一点点的去看引用关系,其实可以用 webpack 提供的一个官方工具来做这件事。</p> <p>首先,你需要这么启动 webpack</p> <pre> <code class="language-javascript">webpack --profile --json > stats.json </code></pre> <p>此时会生成一个 stats.json 文件,之后在官方分析工具里上传文件即可对你的bundle进行分析。</p> <p>这里我用官方的例子简单说下</p> <ol> <li> <p>上传你的 json 文件,长传后会看到这么一个界面,会简单描述你的 webpack 的版本,有多少 modules ,多少 chunks 等等</p> <img src="https://simg.open-open.com/show/765b76b0ba8616a68be625ba662a2bb3.png"></li> <li> <p>点击 chunks ,可以看到所有 chunks 的描述,左边是 chunks 的id,然后有 namse ,有多少 modules ,大小,引用它的 chunks 是谁、即 parents ,假如我们需要分析 id 为1的 chunk ,只需要点击左边的 id<br> <img src="https://simg.open-open.com/show/f5ecc566d9ccec5e8d6deb18b0d5d5c9.png"></p> </li> <li> <p style="text-align:center">这里你可以看到更详细的信息,这里最重要的是两个, reasons 是引用这个 chunks 的模块, modules 是这个 chunks 所引用的 modules<br> <img src="https://simg.open-open.com/show/f86ea740fa7d7feb4f185f1b905cf742.png"></p> </li> <li> <p>这里你发现有一个模块不是你想要的 modules ,你只需要点击这个模块的id,再去查看 reasons 就可以看到这个模块是被谁引入的</p> </li> </ol> <h2>如何优化本地开发体验和打包速度</h2> <p>webpack吐槽的常态 —— 打包慢,这里说一下我们这边做过的优化。</p> <h3>缩小文件搜索范围</h3> <p>将 resolve.modules 配置为 node_modules ,像使用 impot _ from "lodash" 这种时webpack遍历向上递归查到 node_modules ,但通常只有一个 node_modules ,为了减少可以直接写明 node_modules 的地址</p> <p>loader 也可以设置需要生效的目录地址</p> <p>比如 babel 的 loader 可以只对src目录里的代码进行编译,忽略庞大的node_modules</p> <pre> <code class="language-javascript">{ test:/\.js$/, loader:'babel-oader', include:path.resolve(__dirname,'src') } </code></pre> <h3>使用alias</h3> <p>发布到npm的库大多包含两个目录,一个是放cmd模块化的lib目录,一个是所有文件合并成的dist目录,多数入口文件是指向lib的。默认情况下webpack会去读lib目录下的入口文件再去递归加载其他以来的文件,这个过程非常耗时, alias 可以让webpack直接使用dist目录的整体文件减少递归</p> <h3>使用noParse</h3> <p>有些库是自成一体,不需要依赖别的库的,webpack无需解析他们的依赖,可以配置这些文件脱离Webpack解析。</p> <h3>happyPack</h3> <p>happyPack的文档也写的很好,就不复制粘贴了,大家可以自行去阅读文档,简单地说,它主要是利用多进程+缓存使得 build 更快,这大幅减少了我们在编译机上编译的时间。</p> <h2>后评估</h2> <p>先说说优化完后的结果,由于 react 的体积过大, lib 就有60k+,基本已经不能继续优化了,加上我们的路由设计的很不好,首屏的bundle依然有70k+,总的来说,首屏从280k降低到140k左右。</p> <p>但是,根据监控的效果来看,页面js下载的总体时间和白屏时间都只降低了30%左右,这并不符合我的心理预期,想了想除了http请求变多以外并没有别的副作用,后续会继续深入的分析一下为什么优化的效果没达到预期。</p> <p> </p> <p>来自:http://zhangfe.github.io/2017/03/12/webpack优化三两事/</p> <p> </p>
本文由用户 PNYLilia 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!