React首頁加載速度優(yōu)化小結(jié)
背景
本文將以一個什么優(yōu)化都沒有的加載比較慢的React項(xiàng)目為例,一步一步探索怎樣提升React首次打開項(xiàng)目首頁時加載特別慢的問題。
問題分析
首先用網(wǎng)頁控制臺查看了一下網(wǎng)頁加載的進(jìn)度,定位到了加載速度很慢的地方

發(fā)現(xiàn)下載網(wǎng)頁的js文件,有15mb大小,用了8秒,然后看了下打包好的build文件夾,有19.1mb,并且沒有分包,是一個js文件。
問題探索
代碼精簡自查
我首先想到的是刪除每個js文件中沒有使用到的導(dǎo)入,簡化一下文件依賴結(jié)構(gòu),再刪除一些不需要的代碼,清理一下用不到的圖片素材之類的。
刪除這個的時候,我沒找到什么好辦法,我使用的是vscode自帶的功能,自動整理所有未使用的import。
使用方法:
打開左上角,文件-首選項(xiàng)-設(shè)置,輸入“setting”,選擇“字體”,點(diǎn)擊“在settings.json中編輯”,在json文件中輸入
"editor.codeActionsOnSave": { "source.organizeImports": true },
輸入這個之后,每個js文件,在保存時會自動去除沒有使用的import。
全部刪除之后,build完,小了一點(diǎn),但依然還有17.7mb,這很顯然還是不滿足要求。
刪除沒用的包
然后我再想著刪除不需要的依賴項(xiàng),這里用到了一個小工具,可以查看哪些包未被使用。
# 安裝 npm install -g depcheck # 安裝完成后輸入該命令查看為使用的包 depcheck # 對于不需要的包,使用以下命令刪除 npm uninstall xxx

找到不需要的包之后,去package.json里面把對應(yīng)的包全部刪除。
然后使用npm install命令,就可以重新整理依賴了。

可以看到刪了很多包,我再重新build一個試試。構(gòu)建完了,還是有17.6mb,并沒有起到什么作用
刪除sourceMap
在瀏覽網(wǎng)頁的過程中,我看到一種解決措施,可以將打包好的build文件夾中的map文件去掉,這樣會讓整體變小很多
所以我又查詢了map文件是做什么的
打包后產(chǎn)生后綴名為.map的文件是由于配置了sourcemap選項(xiàng)生成的,打包后的文件不容易找到出bug對應(yīng)的源代碼的位置,sourcemap就是來幫我們解決這個問題的,有了map就可以像未壓縮的代碼一樣,準(zhǔn)確的輸出是哪一行哪一列有錯。
可以看到map是用來在報錯時定位源碼位置的,是個不錯的功能,但我打包好的build文件夾一共17mb,其中map文件就占了12mb
所以生產(chǎn)模式部署到服務(wù)器上,如果map文件太大還是刪除了比較好
以下是我查到的一種比較合適的刪除map文件的方法:
因?yàn)閣ebpack的代碼中在打包時會自動讀取env文件
// 加載環(huán)境變量
require('../config/env')
所以我們可以利用這個性質(zhì),在根目錄下創(chuàng)建一個.env的文件,并在里面輸入

# 該變量用于控制讓 npm run build 是否會生成map文件,為false則不生產(chǎn),true則生成 GENERATE_SOURCEMAP = false
這樣設(shè)置完之后,再次打包build,發(fā)現(xiàn)文件大小只剩下5mb了
Terser壓縮
然后我又搜索了相關(guān)資料,我使用的打包是webpack5,發(fā)現(xiàn)可以使用TerserWebpackPlugin對代碼進(jìn)行壓縮,它不僅可以壓縮,還可以順便進(jìn)行代碼混淆,改所有變量名,去掉所有縮進(jìn)空格然后壓縮到一行里
閱讀了webpack的官方文檔之后,我對插件配置進(jìn)行了一些調(diào)整(官方文檔:https://webpack.docschina.org/plugins/terser-webpack-plugin/)
在webpack.config.js中進(jìn)行相關(guān)配置
// 這是我TerserPlugin打包的相關(guān)配置,在optimization下進(jìn)行配置
new TerserPlugin({
parallel: true, // 并行打包,打包加速
extractComments: false, // 刪掉注釋,若為true,表示會將注釋抽取到一個單獨(dú)的文件中
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
toplevel: true,
mangle: true,
keep_classnames: false,
keep_fnames: false,
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
這樣設(shè)置之后再次打包,發(fā)現(xiàn)build文件夾只有4.35mb了,已經(jīng)比一開始小很多了,但感覺加載速度還是不會非常流暢。
SplitChunksPlugin分包
Code Splitting拆包優(yōu)化的最終目標(biāo)是什么?
- 把更新頻率低的代碼和內(nèi)容頻繁變動的代碼分離,把共用率較高的資源也拆出來,最大限度利用瀏覽器緩存。
- 減少 http 請求次數(shù)的同時避免單個文件太大以免拖垮響應(yīng)速度,也就是拆包時盡量實(shí)現(xiàn)文件個數(shù)更少、單個文件體積更小。
這是這部分的官方文檔:https://webpack.docschina.org/plugins/split-chunks-plugin
以下是我的配置
optimization: {
splitChunks: {
chunks: 'initial',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
這樣可以將重復(fù)使用的一些js文件比如組件庫的代碼,拆成多份,按需分塊加載
路由按需懶加載
在React16.6.0版本中,新增了React.lazy函數(shù),它能讓你像常規(guī)組件一樣處理動態(tài)引入的組件,配合 webpack 的 Code Splitting,只有當(dāng)組件被加載,對應(yīng)的資源才會導(dǎo)入,從而達(dá)到懶加載的效果。
一般懶加載和動態(tài)路由一起使用,本文以靜態(tài)路由舉例,動態(tài)路由多了個動態(tài)生成路由的過程,技術(shù)層面本質(zhì)對優(yōu)化有用的還是懶加載。
// 路由懶加載demo
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const TestPage= lazy(() => import('./routes/TestPage'));
const App = () => (
<BrowserRouter>
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route exact path="/" component={Home}/>
<Route path="/testPage" component={TestPage}/>
</Routes>
</Suspense>
</Router>
</BrowserRouter>
)
我使用的React版本是18.2.0,只需要按上面的寫法,就可以直接實(shí)現(xiàn)代碼分塊+路由懶加載了,其余設(shè)置在creat-react-app的時候已經(jīng)自動配置好相關(guān)文件了
這樣實(shí)現(xiàn)的懶加載其中代碼分包已經(jīng)集成進(jìn)去了,webpack會根據(jù)你路由懶加載這部分的分塊,自動對代碼進(jìn)行拆包。
PS:其中有一點(diǎn)需要注意的是,<Suspense>一定是在<Router>的下級,Route或Routes的上級,不能放在Routes和Route之間,因?yàn)檫@個問題我卡了大半天,一開始看著demo隨便放的,沒在意,結(jié)果報錯在網(wǎng)上查了半天也查不到,最后自己沒注意試出來的。

這樣弄完之后就沒有大的代碼塊了,只需讀取較小的文件,就能顯示出主頁來,其余部分按需懶加載,用戶體驗(yàn)感upup
小技巧
還有個小技巧,如果是在路由的地方,頁面級別的懶加載,如果直接按上文那么寫的話,在切換頁面的時候會閃爍一下,因?yàn)橛袀€懶加載的過程,就算加載的再快也會閃爍一下<Suspense>組件未加載完等待的效果。
這本身是沒什么問題的,切頁面閃一下,但如果你當(dāng)前頁面有不希望閃爍的地方,它會跟著一起閃爍,無論那部分你是否設(shè)置了懶加載
舉個例子,就比如你路由用到了Layout,如下圖,你的<Suspense>組件不能放在Route中間,只能整個包裹Routes,所以要閃爍它都會一起閃,這樣給用戶的體驗(yàn)就會很差

解決方法:
把每個需要懶加載的頁面使用函數(shù)式組件包裝一下,把<Suspense>換個位置放,這樣layout就不會跟著其他懶加載頁面一起閃了
# 函數(shù)部分
const lazyComponent = (element) => {
return (
<Suspense fallback={<p>loading...</p>}>
{element}
</Suspense>
)
}
# ----------------------------------------------------------------------------
# return的ReactNode部分
<Route element={<StuLayout />}>
<Route path='/StuIndex' index element={lazyComponent(<StuIndex />)} />
<Route path='/StuIndex/ViewAnn' element={lazyComponent(<ViewAnn />)} />
</Route>
到此這篇關(guān)于React首頁加載速度優(yōu)化小結(jié)的文章就介紹到這了,更多相關(guān)React首頁加載速度內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React使用高階組件與Hooks實(shí)現(xiàn)權(quán)限攔截教程詳細(xì)分析
高階組件就是接受一個組件作為參數(shù)并返回一個新組件(功能增強(qiáng)的組件)的函數(shù)。這里需要注意高階組件是一個函數(shù),并不是組件,這一點(diǎn)一定要注意,本文給大家分享React高階組件使用小結(jié),一起看看吧2023-01-01
用react-redux實(shí)現(xiàn)react組件之間數(shù)據(jù)共享的方法
這篇文章主要介紹了用react-redux實(shí)現(xiàn)react組件之間數(shù)據(jù)共享的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
React Form組件的實(shí)現(xiàn)封裝雜談
這篇文章主要介紹了React Form組件的實(shí)現(xiàn)封裝雜談,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
React實(shí)現(xiàn)一個高度自適應(yīng)的虛擬列表
這篇文章主要介紹了React如何實(shí)現(xiàn)一個高度自適應(yīng)的虛擬列表,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下2021-04-04
React Hooks - useContetx和useReducer的使用實(shí)例詳解
這篇文章主要介紹了React Hooks - useContetx和useReducer的基本使用,本文通過實(shí)例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-11-11

