写在前头--在公司搬砖也差很少一年了,眼看着项目愈来愈大,优化问题亟待解决。优化是一件很矛盾的事情,可是为了诗和远方,咱们仍是得走一趟坑坑洼洼的优化之路。css
本文可能涉及的内容--html
整个项目大概有60+个页面,用到的组件大概150+,package里面的依赖大概有70+个,应该勉强算得上是一个中型的React的项目了。react
下面给你们看看咱们如今build一次项目的结果--webpack
打包时间约150s,打包完以后的资源gzip以后约1.2m,尽管以前分离了一些公用依赖,可是index包的体积达到了600+仍是使人难以接受的。css3
开始优化以前,最重要的就是搞清楚咱们到底要优化什么。肯定了优化的目标才能着手思考优化方案,进而实施优化方案。结合对项目的bundle分析以及自身对项目的了解,咱们初步能够定出如下几点优化方向--git
首先咱们须要足够了解咱们的项目,才能着手进行瘦身。在这里有一个很给力的工具能够推荐给你们webpack-bundle-analyzeres6
盗用一下github上的图github
如图所示,咱们能够很清晰的看到每一个js文件里的module组成,还能够看到每一个module的大小以及module的组成成分,这对咱们分析代码冗余以及优化方向都可以提供很大的帮助。web
具体食用方式也很简单--json
这样一来咱们就对项目有了一个比较具体的认识,大到项目的依赖一览,小到某个页面的组件引用都能在分析报告中找到。接下来就能够开始咱们的瘦身之旅了。
当咱们第一次看到bundle的分析报告时,总能找到一些出乎意料的“大个子”,若是是必不可缺的依赖则没办法,但若是是一些能够被取代的依赖就有别的说法了。这里恰好能够看看以前我对create-react-app中moment.js依赖的处理,若是处理顺利的话能够很直观的看到bundle大小的变化。
总结来讲,若是有小的而且知足需求的依赖能够替换,请不要迟疑;但若是没有可知足的依赖,能够尝试本身造一个轮子,固然后者须要结合自身情况考虑。
相信你们在开发网站的时候都用到了很多依赖,可是这些依赖在输出以后是和业务代码打包在一块儿的,这个明显不符合咱们的预期。面对这些基本不会变动的依赖,咱们更倾向它们可以主动抱团而且远离咱们的业务代码。
这时候,就该使用webpack的CommonsChunkPlugin(貌似在wp4中已经被别的插件替代了,在此咱们先不讨论wp4),它能够帮助咱们将一些指定的module打包进指定的bundle里。具体使用方案能够参考wp官网中的相关介绍,有一个坑点就是--假若但愿负责集合依赖bundle的文件名在打包时不变,则须要生成manifest。
咱们能够将一些低频页面完全拉出项目,拿咱们的项目来讲,一共60+个页面,用户大多都是只会访问其中的几个或十几个页面,不可能将全部的页面都访问一遍。这样一来就必然会有一些页面的访问频率相比之下会十分低下,该怎么处理这些页面呢?这里有几种方案:
以上三种方案我的以为各有优劣,第一个方案很简单
,适用场景就是相似Q&A的静态页,能够将其脱离项目,写成静态页部署在其余的地方,可是很差的地方就是可能无法复用原项目组件。
方案二呢则是时间至上
的方案,能够作到快速迁移,可是很差的地方在于迁移出去的页面其实仍是塞在一个篮子里,只不过换了个新的篮子罢了。
方案三则是质量至上
的方案,以时间做为成本,换来一个新的“低频页面项目”。具体要使用哪一种方案,我以为也是根据当前项目情况而定,不追求最完美,追求最合适。
首屏加载,大概是优化的永恒话题,全部的优化都避不开这一个话题,由于只有它能最直观的让“你们”都感觉到咱们此次优化的成果。对于用户来讲,认为网页首屏很快的标准其实很单一,就是一打开页面,看了多久的白屏。因此咱们须要作的就是弱化用户对白屏的感知
,围绕这一点,我的认为,首屏加载这一优化能够有两个方向:一个是速度,另外一个是体验。
其实这个就是前段时间很火的“骨架屏”,咱们能够在页面真正被渲染出来以前,先给用户看到一个“假的”页面,等到某个时间节点(例如数据已经准备完毕...)就将真正的内容替换上去。这里有一个我写的很不走心的例子:)
在这个优惠券列表页面个人处理方案是,初始化页面的时候就渲染3个列表项骨架,等待接口数据返回就将真实内容替换上去。
在咱们的首屏其实也是相似的,咱们能够根据首屏的展现结构,作一个匹配的骨架组件,而后按需求进行展现便可,这样能够有效减小用户看到白屏的时间。下面是我这个骨架的代码,优化的空间很大,不过因为优先级不是很高,因此就没有进行迭代了。
大概结构就是这样,样式方面很粗暴,由于每一项都是独立的一个组件,直接能够用absolute定位堆砌一个简洁的占位列表项。里面那个相似进度条的效果则是经过css3的animation实现的,咱们能够将每一个block的背景色变成渐变的,而后经过background-positon的变化来达到图中的效果。
这应该是个老生常谈的优化方向了,原理大概是将视图以外的图片都用同一个占位图进行占位,将其真正的图连接存在data-*中,经过监听滚动来判断图片是否进入视图中,来控制img标签src的值。具体的实现不少地方都能搜到,你们能够根据自身状况,按需选择。
其实在整个优化过程当中个人重心是放在这个地方的,其余的都是半路上想到的...
让咱们回想一下,上面咱们讲过将低频页面分离,那么,必然就有会那么几个访问量十分高的页面,那么对于这几个页面应该怎么办呢?
由于访问频率高,因此咱们能够认为这些页面与咱们的核心业务是强相关的,因此将其分离就显得不那么划算了(极可能会出现维护多套代码的窘况)。
可是这样高频页面才是优化的重点区域呀,应该怎么办呢?面对这样页面咱们仍是可使用懒加载大法(页面懒加载 || 组件懒加载 || 依赖懒加载)。
想要在js层面实现各种懒加载,咱们都须要借助webpack中的特性Code Splitting,它能够将咱们原本打包在一块儿的js分解成一块一块,并能达到按需加载并使用的效果。
由于咱们使用了react-router,因此咱们可使用react-router的getComponent轻松达到页面懒加载这一需求。以下图所示,将mainpage这样引入route的话,在打包的时候会将其分离成一个独立的js。
组件和依赖的懒加载也是十分简单的,以下图这样写就能达到懒加载的效果,但若是咱们使用了babel则须要修改一下babel的配置,让它可以顺利解析动态import()的语法。
咱们一般的优化都是为了用户而优化,但其实为了咱们自身可以良好的开发体验,也应该为开发人员优化优化开发体验,打包优化则成了不二之选。
因为时间缘由,在公司的项目中并无尝试使用DLL,可是看到网上有很多同窗都推荐介绍了它,因此我选择在此说起一下~有关于webpack DLL的文档
因为项目是在差很少一年多之前正式启动的,因此接手的时候是webpack1.x,在刚接手的时候为了懒加载硬是升级到了2.x。
可是到如今发现,2.x好像也不够用了,毕竟已经落下了两个大版本了,更新以后的新特性、新功能或是新优化都应该成为我将项目迁移至新版本的动力。wp4具体的配置细节,在掘金上就见到过挺多同窗介绍的,这里我想介绍一下,我是怎么将旧项目迁移到wp4的:
在开始进行版本迁移以前,我设想了两个方案,一个是在原有项目上直接升级并修改配置;第二个方案是新建webpack4项目,搭建好以后将业务代码迁移过来。
通过对成功率以及时间成本的评估,我最后选择的是第二个方案。那么这个新建的项目应该完善到什么程度才能进行迁移呢?我我的是通过如下几个步骤--
这回的项目和以前的都不太同样了,咱们没有借助大神们的脚手架来搭建项目骨架了,咱们须要本身从零开始一点一点的摸索webpack的用法以及新旧版本的差别。
关于一些基础的知识以及配置十分推荐查阅webpack官网的文档以及一些以前参考过的文章webpack4-用之初体验、webpack 4.0.0-beta.0 新特性介绍。
相信你们看完这些以后都会对wp的配置有基本的认知,紧接下来就是建目录、装依赖巴拉巴拉。最终咱们会获得一个这样的目录结构--
咱们应该如何判断将项目代码迁入新项目的时机呢?很简单,当这个新项目能够正常的调试或打包一个相应框架的Hello World便可。
拿咱们的项目来讲,搭建完项目目录以及一些基础配置以后,接下来就是彻底模拟原有项目的技术栈,在新的项目中写几个简单的demo页。固然这些demo页并非随便写的,是带有目的性的,按我此次的经从来说,我写了这么几个文件index.js、App.js、Hello.js、Global.scss、router.js。
剥开非核心依赖,咱们最核心的依赖其实就是react & react-router & sass,只要webpack可以正确的解析es6和sass咱们就能很大程度的还原旧项目的环境。(babel中与jsx相关的配置在package里)
上面的demo完成以后,咱们新项目就初具雏形了,接下来咱们就须要将旧项目package.json迁移到新项目中,这里须要注意的几点是:
① "scripts"中的指令要注意,咱们要看里面的每条指令分别有什么做用,而后再思考应该怎么在新项目中写一个功能同样的指令。
② "dependencies" && "devDependencies"旧项目的依赖也应该无缝迁移过来,不过咱们能够趁这个机会把没有用到的依赖剔除出去。
③ "babel" || "autoprefixer"等辅助工具的配置也应该与旧项目保持一致。
上述步骤都跑通以后,就能删掉原有的demo,将旧项目的全部业务代码都迁移过来。接下来就看着报错,一个一个修复便可。这里遇到这么几个坑,困扰了我许久。页面很顺利的迁移过来了,依赖补全以后也顺利的跑起来了。
可是在dev环境下切换页面总是会404。相信你们看到这里就懂了,我用了history模式的路由,在devServer中应该要加上这样一行配置
devServer { historyApiFallback: true } 复制代码
好啦,404消灭以后又有新的情况了,静态资源总是引用不到,这是为啥?其实这是由于咱们在output的时候没有设置publicPath引发的,在dev的webpack.config中个人output是这样配置的
output: { path: path.resolve('dist'), publicPath: '/' }, // ... devServer = { contentBase: './dist', port: 9000, historyApiFallback: true } 复制代码
这个问题解决以后,咱们的开发环境算是还原的差很少了。接下来就该踩踩打包的坑了,我遇到的第一个问题就是,打包完成以后,文件夹里面只有打包输出,index.html咋不见了...这说好的不太同样。后面发现是少了copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin') const config = { // ... plugins: [ new CopyWebpackPlugin([{from: 'public', to: ''}]) ] } 复制代码
加上这个依赖以后,咱们public文件夹的内容就会乖乖的在dist里面出现。但紧接着又出现新的问题了,第二次打包的时候,怎么dist没有被清空呢?和上面同样,年轻的我少用了一个插件clean-webpack-plugin
const CleanWebpackPlugin = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') let cleanOptions = { root: path.join(__dirname, '..'), verbose: true, dry: false } const config = { // ... plugins: [ new CleanWebpackPlugin(['dist'], cleanOptions), new CopyWebpackPlugin([{from: 'public', to: ''}]) ] } 复制代码
完成上述配置后,每次打包webpack都会清空dist文件夹,而且在打包完成以后,将public中的内容复制到dist。好了看来应该能够了,可是在本地开了个服务器跑页面的时候发现,各类静态资源404。这又是什么玩意?实话说在这里踩坑时间是最多的,可是解决方案又是使人窒息的简单...都怪本身没有好好看文档
const CleanWebpackPlugin = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') let cleanOptions = { root: path.join(__dirname, '..'), verbose: true, dry: false } const config = { output: { filename: 'static/js/[name].[chunkhash:8].js', chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', publicPath: 'http://localhost:5000/' // !!!这里必定要使用绝对路径,否则就会被坑到 } // ... plugins: [ new CleanWebpackPlugin(['dist'], cleanOptions), new CopyWebpackPlugin([{from: 'public', to: ''}]) ] } 复制代码
好了,不知道有多少同窗会看到这里,先谢谢你们看我在这唠叨一堆~各种优化的方案在网上看了好多好多,可是好像你们都只讲方案没有涉及实践,等到本身真正去玩的时候才发现,其实优化没有想象中那么简单,要兼顾原有的,又要尽可能使用更新更好的,不少时候都会在夹缝中取舍。
对了,好像忘记放懒加载优化后的图了(仍是wp2打包的),效果可能没有想象中那么理想吧,但也算往前踏出很不容易的一步了:)
其实,可以优化的还有不少不少,请求方面、业务方面甚至是代码写法...都是能够优化的,可是这些怎么能一蹴而就呢?仍是得走一步,看一步,选择最适合自家项目的优化方案才是最佳方案~