react打包后图片丢失_React中型项目的优化实践

2b90e1bfb41e5f749350010daf031c7e.png

本文可能涉及的内容--

9faf2ab4886cd8604fe924d02442f76a.png

项目介绍

整个项目大概有60+个页面,用到的组件大概150+,package里面的依赖大概有70+个,应该勉强算得上是一个中型的React的项目了。

下面给大家看看我们现在build一次项目的结果--

faa4c6cb7cb43978877747aca55db1b7.png

打包时间约150s,打包完之后的资源gzip之后约1.2m,尽管之前分离了一些公用依赖,但是index包的体积达到了600+还是令人难以接受的。

需要解决的问题 && 思考过的方案

开始优化之前,最重要的就是搞清楚我们到底要优化什么。确定了优化的目标才能着手思考优化方案,进而实施优化方案。结合对项目的bundle分析以及自身对项目的了解,我们初步可以定出以下几点优化方向--

① 体积瘦身

首先我们需要足够了解我们的项目,才能着手进行瘦身。在这里有一个很给力的工具可以推荐给大家webpack-bundle-analyzer

盗用一下github上的图

6c7174acb212bcda1cf4c767169d5e53.gif

如图所示,我们可以很清晰的看到每个js文件里的module组成,还可以看到每个module的大小以及module的组成成分,这对我们分析代码冗余以及优化方向都能够提供很大的帮助。

具体食用方式也很简单--

d1e6c7b2a2168ad3bf78ebe87eeaa847.png

这样一来我们就对项目有了一个比较具体的认识,大到项目的依赖一览,小到某个页面的组件引用都能在分析报告中找到。接下来就可以开始我们的瘦身之旅了。

打团先找大哥

当我们第一次看到bundle的分析报告时,总能找到一些出乎意料的“大个子”,如果是必不可缺的依赖则没办法,但如果是一些可以被取代的依赖就有别的说法了。这里刚好可以看看之前我对create-react-app中moment.js依赖的处理,如果处理顺利的话可以很直观的看到bundle大小的变化。

总结来说,如果有小的并且满足需求的依赖可以替换,请不要迟疑;但如果没有可满足的依赖,可以尝试自己造一个轮子,当然后者需要结合自身状况考虑。

分散站位

相信大家在开发网站的时候都用到了不少依赖,但是这些依赖在输出之后是和业务代码打包在一起的,这个明显不符合我们的预期。面对这些基本不会变更的依赖,我们更倾向它们能够主动抱团并且远离我们的业务代码。

这时候,就该使用webpack的CommonsChunkPlugin(貌似在wp4中已经被别的插件替代了,在此我们先不讨论wp4),它可以帮助我们将一些指定的module打包进指定的bundle里。具体使用方案可以参考wp官网中的相关介绍,有一个坑点就是--倘若希望负责集合依赖bundle的文件名在打包时不变,则需要生成manifest。

不要将页面都放到一个篮子里(一)-- 页面分离

我们可以将一些低频页面彻底拉出项目,拿我们的项目来说,一共60+个页面,用户大多都是只会访问其中的几个或十几个页面,不可能将所有的页面都访问一遍。这样一来就必然会有一些页面的访问频率相比之下会十分低下,该怎么处理这些页面呢?这里有几种方案:

  • 脱离框架重写相关页面并重新部署
  • Copy项目代码,在其他地方重新跑一次并部署,原项目就可以删除不需要的页面
  • 上一方案的简化版,复刻项目环境,跑一个新的纯净项目并部署,将原项目低频页面“剪切”到新的项目中

以上三种方案个人觉得各有优劣,第一个方案很简单,适用场景就是类似Q&A的静态页,可以将其脱离项目,写成静态页部署在其他的地方,但是不好的地方就是可能没法复用原项目组件。

方案二呢则是时间至上的方案,可以做到快速迁移,但是不好的地方在于迁移出去的页面其实还是塞在一个篮子里,只不过换了个新的篮子罢了。

方案三则是质量至上的方案,以时间作为成本,换来一个新的“低频页面项目”。具体要使用哪种方案,我觉得也是根据当前项目状况而定,不追求最完美,追求最合适。

② 首屏加载

首屏加载,大概是优化的永恒话题,所有的优化都避不开这一个话题,因为只有它能最直观的让“大家”都感受到我们这次优化的成果。对于用户来说,认为网页首屏很快的标准其实很单一,就是一打开页面,看了多久的白屏。所以我们需要做的就是弱化用户对白屏的感知,围绕这一点,个人认为,首屏加载这一优化可以有两个方向:一个是速度,另一个是体验。

引入加载占位

其实这个就是前段时间很火的“骨架屏”,我们可以在页面真正被渲染出来之前,先给用户看到一个“假的”页面,等到某个时间节点(例如数据已经准备完毕...)就将真正的内容替换上去。这里有一个我写的很不走心的例子:)

0ae12f08b42b233b41d6e549f9fae2f2.gif

在这个优惠券列表页面我的处理方案是,初始化页面的时候就渲染3个列表项骨架,等待接口数据返回就将真实内容替换上去。

在我们的首屏其实也是类似的,我们可以根据首屏的展示结构,做一个匹配的骨架组件,然后按需求进行展示即可,这样可以有效减少用户看到白屏的时间。下面是我这个骨架的代码,优化的空间很大,不过由于优先级不是很高,所以就没有进行迭代了。

d3aef103afb71c6ffe9d0d03c543bc54.png

大概结构就是这样,样式方面很粗暴,因为每一项都是独立的一个组件,直接可以用absolute定位堆砌一个简洁的占位列表项。里面那个类似进度条的效果则是通过css3的animation实现的,我们可以将每个block的背景色变成渐变的,然后通过background-positon的变化来达到图中的效果。

图片懒加载

这应该是个老生常谈的优化方向了,原理大概是将视图之外的图片都用同一个占位图进行占位,将其真正的图链接存在data-*中,通过监听滚动来判断图片是否进入视图中,来控制img标签src的值。具体的实现很多地方都能搜到,大家可以根据自身情况,按需选择。

不要将页面都放到一个篮子里(二)-- 懒加载

其实在整个优化过程中我的重心是放在这个地方的,其他的都是半路上想到的...

让我们回想一下,上面我们讲过将低频页面分离,那么,必然就有会那么几个访问量十分高的页面,那么对于这几个页面应该怎么办呢?

因为访问频率高,所以我们可以认为这些页面与我们的核心业务是强相关的,所以将其分离就显得不那么划算了(很可能会出现维护多套代码的窘况)。

但是这样高频页面才是优化的重点区域呀,应该怎么办呢?面对这样页面我们还是可以使用懒加载大法(页面懒加载 || 组件懒加载 || 依赖懒加载)。

想要在js层面实现各类懒加载,我们都需要借助webpack中的特性Code Splitting,它可以将我们本来打包在一起的js分解成一块一块,并能达到按需加载并使用的效果。

  • 页面懒加载

因为我们使用了react-router,所以我们可以使用react-router的getComponent轻松达到页面懒加载这一需求。如下图所示,将mainpage这样引入route的话,在打包的时候会将其分离成一个独立的js。

0db2592e396c5f8b94c1bb0e71684731.png
  • 组件懒加载 && 依赖懒加载

组件和依赖的懒加载也是十分简单的,如下图这样写就能达到懒加载的效果,但如果我们使用了babel则需要修改一下babel的配置,让它能够顺利解析动态import()的语法。

2a1e956f84a86bb3e84d3fdd68ca8eac.png

③ 打包提速

我们通常的优化都是为了用户而优化,但其实为了我们自身能够良好的开发体验,也应该为开发人员优化优化开发体验,打包优化则成了不二之选。

使用DLL为打包保驾护航

由于时间原因,在公司的项目中并没有尝试使用DLL,但是看到网上有不少同学都推荐介绍了它,所以我选择在此提及一下~有关于webpack DLL的文档

将webpack版本从2.0 --> 4.x

由于项目是在差不多一年多以前正式启动的,所以接手的时候是webpack1.x,在刚接手的时候为了懒加载硬是升级到了2.x。

但是到现在发现,2.x好像也不够用了,毕竟已经落下了两个大版本了,更新之后的新特性、新功能或是新优化都应该成为我将项目迁移至新版本的动力。wp4具体的配置细节,在掘金上就见到过挺多同学介绍的,这里我想介绍一下,我是怎么将旧项目迁移到wp4的:

在开始进行版本迁移之前,我设想了两个方案,一个是在原有项目上直接升级并修改配置;第二个方案是新建webpack4项目,搭建好之后将业务代码迁移过来。

经过对成功率以及时间成本的评估,我最后选择的是第二个方案。那么这个新建的项目应该完善到什么程度才能进行迁移呢?我个人是经过以下几个步骤--

  • 搭建项目骨架

这回的项目和之前的都不太一样了,我们没有借助大神们的脚手架来搭建项目骨架了,我们需要自己从零开始一点一点的摸索webpack的用法以及新旧版本的差异。

关于一些基础的知识以及配置十分推荐查阅webpack官网的文档以及一些之前参考过的文章webpack4-用之初体验、webpack 4.0.0-beta.0 新特性介绍。

相信大家看完这些之后都会对wp的配置有基本的认知,紧接下来就是建目录、装依赖巴拉巴拉。最终我们会得到一个这样的目录结构--

feaec828d833453992fb30eb2eccb5ec.png
  • 写个Hello World!

我们应该如何判断将项目代码迁入新项目的时机呢?很简单,当这个新项目可以正常的调试或打包一个相应框架的Hello World即可。

拿我们的项目来说,搭建完项目目录以及一些基础配置之后,接下来就是完全模拟原有项目的技术栈,在新的项目中写几个简单的demo页。当然这些demo页并不是随便写的,是带有目的性的,按我这次的经历来说,我写了这么几个文件index.js、App.js、Hello.js、Global.scss、router.js。

剥开非核心依赖,我们最核心的依赖其实就是react & react-router & sass,只要webpack能够正确的解析es6和sass我们就能很大程度的还原旧项目的环境。(babel中与jsx相关的配置在package里)

11495e4293b88832b42e4b75a20629ee.png
  • 仔细研读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打包的),效果可能没有想象中那么理想吧,但也算往前踏出很不容易的一步了:)

ce85976b15f875d8ce340a2ffd447fec.png

其实,能够优化的还有很多很多,请求方面、业务方面甚至是代码写法...都是可以优化的,但是这些怎么能一蹴而就呢?还是得走一步,看一步,选择最适合自家项目的优化方案才是最佳方案~

acc1a076829a6ef7fffd368cb380f66b.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/335541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cuba.platform_CUBA Platform 6.3的新增功能

cuba.platform我们很自豪地宣布新版本的CUBA平台和Studio全面上市! 也许这是有史以来功能最丰富的平台版本之一–在各个级别上都有重要的变化:体系结构,可扩展性,API可用性和性能。 本文介绍了该平台的主要增强功能。 发行说明中…

如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库

文章目录Windows系统下访问本地MySQL数据库访问远程主机的MySQL数据库本地安装了MySQL数据库本地没有安装MySQLLinux系统下退出数据库登录Windows系统下 访问本地MySQL数据库 使用命令终端访问本地MySQL数据库,打开终端直接输入以下命令语句: mysql …

科大讯飞 jason word_2019科大讯飞全球1024开发者节开幕啦

2019年10月24日,属于开发者的共同节日2019科大讯飞全球1024开发者节于合肥滨湖国际会展中心盛大启幕。以“A.I.前进,拼世界”为主题的本届大会,将集结120位行业大咖主题演讲、20场行业分论坛详解全域A.I.,科大讯飞的1024计划3.0、…

多线程线程池的实现java_如何在Java中实现线程池

多线程线程池的实现java线程是独立程序的执行路径。 在java中,每个线程都扩展java.lang.Thread类或实现java.lang.Runnable。 多线程是指在一个任务中同时执行两个或多个线程。在多线程中,每个任务可以有多个线程,并且这些线程可以异步或同步…

Windows 使用命令执行 sql 脚本文件

文章目录MySQL 数据库方法一:使用 mysql 命令方法二:使用 source 命令Oracle 数据库MySQL 数据库 方法一:使用 mysql 命令 未配置 MySQL 的环境变量,则需要进入 MySQL 的 bin 目录下才能执行 mysql 命令已配置 MySQL 的环境变量…

windows系统连接同局域网下的其他电脑mysql等服务

一、首先设置被连接的mysql服务的 root 用户及访问权限(必须确保连接用户拥有不限ip访问权限) 参考:https://blog.csdn.net/hkl_Forever/article/details/127543546 二、配置被连接电脑的防火墙、网络共享、设置出入端口规则等 1、打开防火墙…

js如何获取jwt信息_谈房地产公众号如何涨粉?一篇文章让你轻松获取信息

原标题:谈房地产公众号如何涨粉?一篇文章让你轻松获取信息对于房地产企业来说,如何学会运营自己企业的房地产微信公众号是涨粉引流的关键一步,老话说:一步走好,步步稳。值得夸奖的是近几年中高端房地产企业…

java开发plc上位机软件开发_上位机开发之西门子PLC-S7通信实践

写在前面:就目前而言,在中国的工控市场上,西门子仍然占了很大的份额,因此对于上位机开发而言,经常会存在需要与西门子PLC进行通信的情况。然后对于西门子PLC来说,通信方式有很多,下面简单列举一…

boss直聘账号异常登不上_【苹果IOS游戏账号分享】据说是最欢乐的2D冒险游戏?Q萌有趣,好不好玩你说了算——惊奇剑士...

惊奇剑士游戏下载点击下方卡片下载游戏!不会使用请点击《操作指南》↓↓↓↓本游戏是由1群的资源库:“hentai是绅士不是变态” 赞助我们购买的,感谢大家的鼎力支持,二哈替大家感谢下hentai是绅士不是变态。可甜可盐的小哥哥之前还…

SQL语句执行顺序

文章目录SQL 简介SQL 语句的执行顺序重点SQL 简介 SQL 是 Structured Query Language 的缩写,称为结构化查询语言,SQL 是一种操作关系型数据库的规则,SQL 语句以分号结尾,不区分大小写,建议关键词使用大写。 SQL 语句…

getexternalfilesdir 相册_音乐相册(电子相册制作)V5.2 安卓最新版

音乐相册(电子相册制作)是一个功能强大的电子相册创建工具。最新版本的音乐专辑应用程序可以帮助用户快速创建精美的多媒体专辑视频,并匹配优美的音乐伴奏,使照片更精彩。音乐专辑应用程序支持几乎所有流行的图片格式,并支持导入和导出&#…

二级java题型及分值_2016年全国计算机二级java语言程序设计考试大纲

2016年全国计算机二级java语言程序设计考试大纲2016年3月计算机考试即将在3月26日-29日进行,为了帮助大家顺利通过2016年计算机二级考试,下面百分网小编为大家带来2016年全国计算机二级java语言程序设计考试大纲,供大家参考学习,预…

linux系统下安装jdk教程

一、首先下载linux版本jdk jdk官网:http://www.oracle.com/technetwork/java/javase/downloads/index.html 根据自己的需求,下载不同版本的jdk 2.将下载好的jdk压缩包,通过ftp上传到linux系统的当前用户下,我当前登录的用户为r…

iphone11什么时候上市_hd3手表高仿哪里买 什么时候上市?

hd3手表高仿哪里买 什么时候上市? 导读:hd3手表高仿哪里买zf的万国7日链高仿表怎么样?款式设计非常出色,很多手表的款式,依靠着该性价比的迅速提升来做出的有效转化,年轻人对于复刻表的选择,能拥有的这些细…

Windows 命令终端(CMD)程序员常用的命令

文章目录certutilcddirmd/mkdirrddelreplacechdir/cdattribhelptypemorefindstrrename/renset环境变量扩展echonetstattskilltasklisttaskkillnettreecopystartpauseprintverwinversysteminfowmiclogofftracertroute printtelnetftppingipconfigshutdown远程关机formatchkdskti…

驱动级的自动按键_空调遥控器特殊按键使用方法及注意事项

在上门维修过程中,用户常常由于遥控器使用不当,或者幵启了特定按键引起空调器的假性故障,这时需要进行电话解答或上门讲解,现对空调遥控器的特殊功能按键作简单介绍。示例遥控器型号为格力YAD0F,与KFR-23GW/ ( 23570 ) Aa-3挂式空…

净资产滚动率_净资产的结构

净资产滚动率Netty的包装结构很棒。 每个程序员都应该研究它。 每个系统都应该模仿它; 每个项目经理都应将其打印出来,拍在墙上,然后对开发人员说:“那样。” Netty是一个“ ...异步事件驱动的网络应用程序框架,用于…

Java从键盘输入若干数_用java编程序:从键盘输入若干个整数,输出这些数中大于其平均值的数。...

展开全部引用1楼 christal yhy的答案,考虑到浮点数的存在,特在此基础上优化一下,代码如下:public static void main(String[] args) {Scanner scan new Scanner(System.in);System.out.println("请输入62616964757a686964616fe59b9ee7ad9431333262373933要输入的个数:&qu…

java多线程编程_阿里P8熬到秃头肝出来的:Java多线程+并发编程核心笔记

自学Java的时候,多线程和并发这一块可以说是最难掌握的部分了,很多小伙伴表示需要一些易于学习和上手的资料。所以今天这本「Java并发学习手册.pdf」就是一份集中学习多线程和并发的手册,PDF版,由RedSpider社区的五位大神撰写&…

内外网逻辑隔离物理隔离_隔离域逻辑

内外网逻辑隔离物理隔离在一个设计模式课程中,我对建模域逻辑进行了有趣的讨论。 具体来说,它是关于隔离域逻辑的 。 一个应用程序通常分为三个部分: 演示(例如桌面GUI,浏览器,Web服务) 域逻辑…