React首页加载速度优化
背景
本文将以一个什么优化都没有的加载比较慢的React项目为例,一步一步探索怎样提升React首次打开项目首页时加载特别慢的问题。
问题分析
用网页控制台查看了一下网页加载的进度,定位到了加载速度很慢的地方
发现下载网页的js文件,有15mb大小,用了8秒,然后看了下打包好的build文件夹,有19.1mb,并且没有分包,是一个js文件。
问题探索
代码精简自查
我首先想到的是删除每个js文件中没有使用到的导入,简化一下文件依赖结构,再删除一些不需要的代码,清理一下用不到的图片素材之类的。
删除这个的时候,我没找到什么好办法,我使用的是vscode自带的功能,自动整理所有未使用的import。
使用方法:
打开左上角,文件-首选项-设置,输入“setting”,选择“字体”,点击“在settings.json中编辑”,在json文件中输入
1 | "editor.codeActionsOnSave": { "source.organizeImports": true }, |
输入这个之后,每个js文件,在保存时会自动去除没有使用的import。
全部删除之后,build完,小了一点,但依然还有17.7mb,这很显然还是不满足要求。
删除没用的包
然后我再想着删除不需要的依赖项,这里用到了一个小工具,可以查看哪些包未被使用。
1 | # 安装 |
找到不需要的包之后,去package.json里面把对应的包全部删除。
然后使用npm install
命令,就可以重新整理依赖了。
可以看到删了很多包,我再重新build一个试试。构建完了,还是有17.6mb,并没有起到什么作用
删除sourceMap
在浏览网页的过程中,我看到一种解决措施,可以将打包好的build文件夹中的map文件去掉,这样会让整体变小很多
所以我又查询了map文件是做什么的
打包后产生后缀名为.map的文件是由于配置了sourcemap选项生成的,打包后的文件不容易找到出bug对应的源代码的位置,sourcemap就是来帮我们解决这个问题的,有了map就可以像未压缩的代码一样,准确的输出是哪一行哪一列有错。
可以看到map是用来在报错时定位源码位置的,是个不错的功能,但我打包好的build文件夹一共17mb,其中map文件就占了12mb
所以生产模式部署到服务器上,如果map文件太大还是删除了比较好
以下是我查到的一种比较合适的删除map文件的方法:
因为webpack的代码中在打包时会自动读取env文件
1 | // 加载环境变量 |
所以我们可以利用这个性质,在根目录下创建一个.env的文件,并在里面输入
1 | # 该变量用于控制让 npm run build 是否会生成map文件,为false则不生产,true则生成 |
这样设置完之后,再次打包build,发现文件大小只剩下5mb了
Terser压缩
然后我又搜索了相关资料,我使用的打包是webpack5,发现可以使用TerserWebpackPlugin对代码进行压缩,它不仅可以压缩,还可以顺便进行代码混淆,改所有变量名,去掉所有缩进空格然后压缩到一行里
阅读了webpack的官方文档之后,我对插件配置进行了一些调整(官方文档:https://webpack.docschina.org/plugins/terser-webpack-plugin/)
在webpack.config.js中进行相关配置
1 | // 这是我TerserPlugin打包的相关配置,在optimization下进行配置 |
这样设置之后再次打包,发现build文件夹只有4.35mb了,已经比一开始小很多了,但感觉加载速度还是不会非常流畅。
SplitChunksPlugin分包
Code Splitting拆包优化的最终目标是什么?
- 把更新频率低的代码和内容频繁变动的代码分离,把共用率较高的资源也拆出来,最大限度利用浏览器缓存。
- 减少 http 请求次数的同时避免单个文件太大以免拖垮响应速度,也就是拆包时尽量实现文件个数更少、单个文件体积更小。
这是这部分的官方文档:https://webpack.docschina.org/plugins/split-chunks-plugin
以下是我的配置
1 | optimization: { |
这样可以将重复使用的一些js文件比如组件库的代码,拆成多份,按需分块加载
路由按需懒加载
在React16.6.0版本中,新增了React.lazy函数,它能让你像常规组件一样处理动态引入的组件,配合 webpack 的 Code Splitting,只有当组件被加载,对应的资源才会导入,从而达到懒加载的效果。
一般懒加载和动态路由一起使用,本文以静态路由举例,动态路由多了个动态生成路由的过程,技术层面本质对优化有用的还是懒加载。
1 | // 路由懒加载demo |
我使用的React版本是18.2.0,只需要按上面的写法,就可以直接实现代码分块+路由懒加载了,其余设置在creat-react-app的时候已经自动配置好相关文件了
这样实现的懒加载其中代码分包已经集成进去了,webpack会根据你路由懒加载这部分的分块,自动对代码进行拆包。
PS:其中有一点需要注意的是,<Suspense>
一定是在<Router>
的下级,Route或Routes
的上级,不能放在Routes和Route
之间,因为这个问题我卡了大半天,一开始看着demo随便放的,没在意,结果报错在网上查了半天也查不到,最后自己没注意试出来的。
这样弄完之后就没有大的代码块了,只需读取较小的文件,就能显示出主页来,其余部分按需懒加载,用户体验感upup
小技巧
还有个小技巧,如果是在路由的地方,页面级别的懒加载,如果直接按上文那么写的话,在切换页面的时候会闪烁一下,因为有个懒加载的过程,就算加载的再快也会闪烁一下<Suspense>
组件未加载完等待的效果。
这本身是没什么问题的,切页面闪一下,但如果你当前页面有不希望闪烁的地方,它会跟着一起闪烁,无论那部分你是否设置了懒加载
举个例子,就比如你路由用到了Layout,如下图,你的<Suspense>
组件不能放在Route中间,只能整个包裹Routes,所以要闪烁它都会一起闪,这样给用户的体验就会很差
解决方法:
把每个需要懒加载的页面使用函数式组件包装一下,把<Suspense>
换个位置放,这样layout就不会跟着其他懒加载页面一起闪了
1 | # 函数部分 |