react项目优化之webpack

#2 · open · 0 comments

View on GitHub ↗

ColaDaddyz

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

Comments