react 判断图片是否加载完成_React中型项目的优化实践
項目介紹
整個項目大概有60+個頁面,用到的組件大概150+,package里面的依賴大概有70+個,應(yīng)該勉強算得上是一個中型的React的項目了。
下面給大家看看我們現(xiàn)在build一次項目的結(jié)果--
打包時間約150s,打包完之后的資源gzip之后約1.2m,盡管之前分離了一些公用依賴,但是index包的體積達(dá)到了600+還是令人難以接受的。
需要解決的問題 && 思考過的方案
開始優(yōu)化之前,最重要的就是搞清楚我們到底要優(yōu)化什么。確定了優(yōu)化的目標(biāo)才能著手思考優(yōu)化方案,進(jìn)而實施優(yōu)化方案。結(jié)合對項目的bundle分析以及自身對項目的了解,我們初步可以定出以下幾點優(yōu)化方向--
① 體積瘦身
首先我們需要足夠了解我們的項目,才能著手進(jìn)行瘦身。在這里有一個很給力的工具可以推薦給大家webpack-bundle-analyzer
盜用一下github上的圖
如圖所示,我們可以很清晰的看到每個js文件里的module組成,還可以看到每個module的大小以及module的組成成分,這對我們分析代碼冗余以及優(yōu)化方向都能夠提供很大的幫助。
具體食用方式也很簡單--
這樣一來我們就對項目有了一個比較具體的認(rèn)識,大到項目的依賴一覽,小到某個頁面的組件引用都能在分析報告中找到。接下來就可以開始我們的瘦身之旅了。
打團(tuán)先找大哥
當(dāng)我們第一次看到bundle的分析報告時,總能找到一些出乎意料的“大個子”,如果是必不可缺的依賴則沒辦法,但如果是一些可以被取代的依賴就有別的說法了。這里剛好可以看看之前我對create-react-app中moment.js依賴的處理,如果處理順利的話可以很直觀的看到bundle大小的變化。
總結(jié)來說,如果有小的并且滿足需求的依賴可以替換,請不要遲疑;但如果沒有可滿足的依賴,可以嘗試自己造一個輪子,當(dāng)然后者需要結(jié)合自身狀況考慮。
分散站位
相信大家在開發(fā)網(wǎng)站的時候都用到了不少依賴,但是這些依賴在輸出之后是和業(yè)務(wù)代碼打包在一起的,這個明顯不符合我們的預(yù)期。面對這些基本不會變更的依賴,我們更傾向它們能夠主動抱團(tuán)并且遠(yuǎn)離我們的業(yè)務(wù)代碼。
這時候,就該使用webpack的CommonsChunkPlugin(貌似在wp4中已經(jīng)被別的插件替代了,在此我們先不討論wp4),它可以幫助我們將一些指定的module打包進(jìn)指定的bundle里。具體使用方案可以參考wp官網(wǎng)中的相關(guān)介紹,有一個坑點就是--倘若希望負(fù)責(zé)集合依賴bundle的文件名在打包時不變,則需要生成manifest。
不要將頁面都放到一個籃子里(一)-- 頁面分離
我們可以將一些低頻頁面徹底拉出項目,拿我們的項目來說,一共60+個頁面,用戶大多都是只會訪問其中的幾個或十幾個頁面,不可能將所有的頁面都訪問一遍。這樣一來就必然會有一些頁面的訪問頻率相比之下會十分低下,該怎么處理這些頁面呢?這里有幾種方案:
- 脫離框架重寫相關(guān)頁面并重新部署
- Copy項目代碼,在其他地方重新跑一次并部署,原項目就可以刪除不需要的頁面
- 上一方案的簡化版,復(fù)刻項目環(huán)境,跑一個新的純凈項目并部署,將原項目低頻頁面“剪切”到新的項目中
以上三種方案個人覺得各有優(yōu)劣,第一個方案很簡單,適用場景就是類似Q&A的靜態(tài)頁,可以將其脫離項目,寫成靜態(tài)頁部署在其他的地方,但是不好的地方就是可能沒法復(fù)用原項目組件。
方案二呢則是時間至上的方案,可以做到快速遷移,但是不好的地方在于遷移出去的頁面其實還是塞在一個籃子里,只不過換了個新的籃子罷了。
方案三則是質(zhì)量至上的方案,以時間作為成本,換來一個新的“低頻頁面項目”。具體要使用哪種方案,我覺得也是根據(jù)當(dāng)前項目狀況而定,不追求最完美,追求最合適。
② 首屏加載
首屏加載,大概是優(yōu)化的永恒話題,所有的優(yōu)化都避不開這一個話題,因為只有它能最直觀的讓“大家”都感受到我們這次優(yōu)化的成果。對于用戶來說,認(rèn)為網(wǎng)頁首屏很快的標(biāo)準(zhǔn)其實很單一,就是一打開頁面,看了多久的白屏。所以我們需要做的就是弱化用戶對白屏的感知,圍繞這一點,個人認(rèn)為,首屏加載這一優(yōu)化可以有兩個方向:一個是速度,另一個是體驗。
引入加載占位
其實這個就是前段時間很火的“骨架屏”,我們可以在頁面真正被渲染出來之前,先給用戶看到一個“假的”頁面,等到某個時間節(jié)點(例如數(shù)據(jù)已經(jīng)準(zhǔn)備完畢...)就將真正的內(nèi)容替換上去。這里有一個我寫的很不走心的例子:)
在這個優(yōu)惠券列表頁面我的處理方案是,初始化頁面的時候就渲染3個列表項骨架,等待接口數(shù)據(jù)返回就將真實內(nèi)容替換上去。
在我們的首屏其實也是類似的,我們可以根據(jù)首屏的展示結(jié)構(gòu),做一個匹配的骨架組件,然后按需求進(jìn)行展示即可,這樣可以有效減少用戶看到白屏的時間。下面是我這個骨架的代碼,優(yōu)化的空間很大,不過由于優(yōu)先級不是很高,所以就沒有進(jìn)行迭代了。
大概結(jié)構(gòu)就是這樣,樣式方面很粗暴,因為每一項都是獨立的一個組件,直接可以用absolute定位堆砌一個簡潔的占位列表項。里面那個類似進(jìn)度條的效果則是通過css3的animation實現(xiàn)的,我們可以將每個block的背景色變成漸變的,然后通過background-positon的變化來達(dá)到圖中的效果。
圖片懶加載
這應(yīng)該是個老生常談的優(yōu)化方向了,原理大概是將視圖之外的圖片都用同一個占位圖進(jìn)行占位,將其真正的圖鏈接存在data-*中,通過監(jiān)聽滾動來判斷圖片是否進(jìn)入視圖中,來控制img標(biāo)簽src的值。具體的實現(xiàn)很多地方都能搜到,大家可以根據(jù)自身情況,按需選擇。
不要將頁面都放到一個籃子里(二)-- 懶加載
其實在整個優(yōu)化過程中我的重心是放在這個地方的,其他的都是半路上想到的...讓我們回想一下,上面我們講過將低頻頁面分離,那么,必然就有會那么幾個訪問量十分高的頁面,那么對于這幾個頁面應(yīng)該怎么辦呢?
因為訪問頻率高,所以我們可以認(rèn)為這些頁面與我們的核心業(yè)務(wù)是強相關(guān)的,所以將其分離就顯得不那么劃算了(很可能會出現(xiàn)維護(hù)多套代碼的窘?jīng)r)。
但是這樣高頻頁面才是優(yōu)化的重點區(qū)域呀,應(yīng)該怎么辦呢?面對這樣頁面我們還是可以使用懶加載大法(頁面懶加載 || 組件懶加載 || 依賴懶加載)。
想要在js層面實現(xiàn)各類懶加載,我們都需要借助webpack中的特性Code Splitting,它可以將我們本來打包在一起的js分解成一塊一塊,并能達(dá)到按需加載并使用的效果。
- 頁面懶加載
因為我們使用了react-router,所以我們可以使用react-router的getComponent輕松達(dá)到頁面懶加載這一需求。如下圖所示,將mainpage這樣引入route的話,在打包的時候會將其分離成一個獨立的js。
- 組件懶加載 && 依賴懶加載
組件和依賴的懶加載也是十分簡單的,如下圖這樣寫就能達(dá)到懶加載的效果,但如果我們使用了babel則需要修改一下babel的配置,讓它能夠順利解析動態(tài)import()的語法。
③ 打包提速
我們通常的優(yōu)化都是為了用戶而優(yōu)化,但其實為了我們自身能夠良好的開發(fā)體驗,也應(yīng)該為開發(fā)人員優(yōu)化優(yōu)化開發(fā)體驗,打包優(yōu)化則成了不二之選。
使用DLL為打包保駕護(hù)航
由于時間原因,在公司的項目中并沒有嘗試使用DLL,但是看到網(wǎng)上有不少同學(xué)都推薦介紹了它,所以我選擇在此提及一下~有關(guān)于webpack DLL的文檔
將webpack版本從2.0 --> 4.x
由于項目是在差不多一年多以前正式啟動的,所以接手的時候是webpack1.x,在剛接手的時候為了懶加載硬是升級到了2.x。
但是到現(xiàn)在發(fā)現(xiàn),2.x好像也不夠用了,畢竟已經(jīng)落下了兩個大版本了,更新之后的新特性、新功能或是新優(yōu)化都應(yīng)該成為我將項目遷移至新版本的動力。wp4具體的配置細(xì)節(jié),在掘金上就見到過挺多同學(xué)介紹的,這里我想介紹一下,我是怎么將舊項目遷移到wp4的:
在開始進(jìn)行版本遷移之前,我設(shè)想了兩個方案,一個是在原有項目上直接升級并修改配置;第二個方案是新建webpack4項目,搭建好之后將業(yè)務(wù)代碼遷移過來。
經(jīng)過對成功率以及時間成本的評估,我最后選擇的是第二個方案。那么這個新建的項目應(yīng)該完善到什么程度才能進(jìn)行遷移呢?我個人是經(jīng)過以下幾個步驟--
- 搭建項目骨架
這回的項目和之前的都不太一樣了,我們沒有借助大神們的腳手架來搭建項目骨架了,我們需要自己從零開始一點一點的摸索webpack的用法以及新舊版本的差異。
關(guān)于一些基礎(chǔ)的知識以及配置十分推薦查閱webpack官網(wǎng)的文檔以及一些之前參考過的文章webpack4-用之初體驗、webpack 4.0.0-beta.0 新特性介紹。
相信大家看完這些之后都會對wp的配置有基本的認(rèn)知,緊接下來就是建目錄、裝依賴巴拉巴拉。最終我們會得到一個這樣的目錄結(jié)構(gòu)--
- 寫個Hello World!
我們應(yīng)該如何判斷將項目代碼遷入新項目的時機呢?很簡單,當(dāng)這個新項目可以正常的調(diào)試或打包一個相應(yīng)框架的Hello World即可。
拿我們的項目來說,搭建完項目目錄以及一些基礎(chǔ)配置之后,接下來就是完全模擬原有項目的技術(shù)棧,在新的項目中寫幾個簡單的demo頁。當(dāng)然這些demo頁并不是隨便寫的,是帶有目的性的,按我這次的經(jīng)歷來說,我寫了這么幾個文件index.js、App.js、Hello.js、Global.scss、router.js。
剝開非核心依賴,我們最核心的依賴其實就是react & react-router & sass,只要webpack能夠正確的解析es6和sass我們就能很大程度的還原舊項目的環(huán)境。(babel中與jsx相關(guān)的配置在package里)
- 仔細(xì)研讀package
上面的demo完成之后,我們新項目就初具雛形了,接下來我們就需要將舊項目package.json遷移到新項目中,這里需要注意的幾點是:
① "scripts"中的指令要注意,我們要看里面的每條指令分別有什么作用,然后再思考應(yīng)該怎么在新項目中寫一個功能一樣的指令。
② "dependencies" && "devDependencies"舊項目的依賴也應(yīng)該無縫遷移過來,不過我們可以趁這個機會把沒有用到的依賴剔除出去。
③ "babel" || "autoprefixer"等輔助工具的配置也應(yīng)該與舊項目保持一致。
- 遷移項目&修修補補
上述步驟都跑通之后,就能刪掉原有的demo,將舊項目的所有業(yè)務(wù)代碼都遷移過來。接下來就看著報錯,一個一個修復(fù)即可。這里遇到這么幾個坑,困擾了我許久。頁面很順利的遷移過來了,依賴補全之后也順利的跑起來了。
但是在dev環(huán)境下切換頁面老是會404。相信大家看到這里就懂了,我用了history模式的路由,在devServer中應(yīng)該要加上這樣一行配置
devServer {historyApiFallback: true }好啦,404消滅之后又有新的狀況了,靜態(tài)資源老是引用不到,這是為啥?其實這是因為我們在output的時候沒有設(shè)置publicPath引起的,在dev的webpack.config中我的output是這樣配置的
output: {path: path.resolve('dist'),publicPath: '/' }, // ... devServer = {contentBase: './dist',port: 9000,historyApiFallback: true }這個問題解決之后,我們的開發(fā)環(huán)境算是還原的差不多了。接下來就該踩踩打包的坑了,我遇到的第一個問題就是,打包完成之后,文件夾里面只有打包輸出,index.html咋不見了...這說好的不太一樣。后面發(fā)現(xiàn)是少了copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin') const config = {// ...plugins: [new CopyWebpackPlugin([{from: 'public', to: ''}])] }加上這個依賴之后,我們public文件夾的內(nèi)容就會乖乖的在dist里面出現(xiàn)。但緊接著又出現(xiàn)新的問題了,第二次打包的時候,怎么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中的內(nèi)容復(fù)制到dist。好了看來應(yīng)該可以了,但是在本地開了個服務(wù)器跑頁面的時候發(fā)現(xiàn),各種靜態(tài)資源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: ''}])] }總結(jié)
好了,不知道有多少同學(xué)會看到這里,先謝謝大家看我在這嘮叨一堆~各類優(yōu)化的方案在網(wǎng)上看了好多好多,但是好像大家都只講方案沒有涉及實踐,等到自己真正去玩的時候才發(fā)現(xiàn),其實優(yōu)化沒有想象中那么簡單,要兼顧原有的,又要盡量使用更新更好的,很多時候都會在夾縫中取舍。
其實,能夠優(yōu)化的還有很多很多,請求方面、業(yè)務(wù)方面甚至是代碼寫法...都是可以優(yōu)化的,但是這些怎么能一蹴而就呢?還是得走一步,看一步,選擇最適合自家項目的優(yōu)化方案才是最佳方案~
總結(jié)
以上是生活随笔為你收集整理的react 判断图片是否加载完成_React中型项目的优化实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 康熙铜钱值多少钱啊?
- 下一篇: 北京环球影城最近的地铁站