react 判斷圖片是否加載完成_React中型項目的優化實踐

f9a2573524b24638b2097274028a8a1b.png

07689abf2f8cef911be8c73e1688d48b.png

項目介紹

整個項目大概有60+個頁面,用到的組件大概150+,package里面的依賴大概有70+個,應該勉強算得上是一個中型的React的項目了。

下面給大家看看我們現在build一次項目的結果--

c6aafad8e365dbd3981b8e8259245abc.png

打包時間約150s,打包完之后的資源gzip之后約1.2m,盡管之前分離了一些公用依賴,但是index包的體積達到了600+還是令人難以接受的。

需要解決的問題 && 思考過的方案

開始優化之前,最重要的就是搞清楚我們到底要優化什么。確定了優化的目標才能著手思考優化方案,進而實施優化方案。結合對項目的bundle分析以及自身對項目的了解,我們初步可以定出以下幾點優化方向--

① 體積瘦身

首先我們需要足夠了解我們的項目,才能著手進行瘦身。在這里有一個很給力的工具可以推薦給大家webpack-bundle-analyzer

盜用一下github上的圖

6ee78dffc244ffc83ae50275ff74d30f.png

如圖所示,我們可以很清晰的看到每個js文件里的module組成,還可以看到每個module的大小以及module的組成成分,這對我們分析代碼冗余以及優化方向都能夠提供很大的幫助。

具體食用方式也很簡單--

0e70aebddbb2cba2746d3f3e7deb6359.png

這樣一來我們就對項目有了一個比較具體的認識,大到項目的依賴一覽,小到某個頁面的組件引用都能在分析報告中找到。接下來就可以開始我們的瘦身之旅了。

打團先找大哥

當我們第一次看到bundle的分析報告時,總能找到一些出乎意料的“大個子”,如果是必不可缺的依賴則沒辦法,但如果是一些可以被取代的依賴就有別的說法了。這里剛好可以看看之前我對create-react-app中moment.js依賴的處理,如果處理順利的話可以很直觀的看到bundle大小的變化。

總結來說,如果有小的并且滿足需求的依賴可以替換,請不要遲疑;但如果沒有可滿足的依賴,可以嘗試自己造一個輪子,當然后者需要結合自身狀況考慮。

分散站位

相信大家在開發網站的時候都用到了不少依賴,但是這些依賴在輸出之后是和業務代碼打包在一起的,這個明顯不符合我們的預期。面對這些基本不會變更的依賴,我們更傾向它們能夠主動抱團并且遠離我們的業務代碼。

這時候,就該使用webpack的CommonsChunkPlugin(貌似在wp4中已經被別的插件替代了,在此我們先不討論wp4),它可以幫助我們將一些指定的module打包進指定的bundle里。具體使用方案可以參考wp官網中的相關介紹,有一個坑點就是--倘若希望負責集合依賴bundle的文件名在打包時不變,則需要生成manifest。

不要將頁面都放到一個籃子里(一)-- 頁面分離

我們可以將一些低頻頁面徹底拉出項目,拿我們的項目來說,一共60+個頁面,用戶大多都是只會訪問其中的幾個或十幾個頁面,不可能將所有的頁面都訪問一遍。這樣一來就必然會有一些頁面的訪問頻率相比之下會十分低下,該怎么處理這些頁面呢?這里有幾種方案:

  • 脫離框架重寫相關頁面并重新部署
  • Copy項目代碼,在其他地方重新跑一次并部署,原項目就可以刪除不需要的頁面
  • 上一方案的簡化版,復刻項目環境,跑一個新的純凈項目并部署,將原項目低頻頁面“剪切”到新的項目中

以上三種方案個人覺得各有優劣,第一個方案很簡單,適用場景就是類似Q&A的靜態頁,可以將其脫離項目,寫成靜態頁部署在其他的地方,但是不好的地方就是可能沒法復用原項目組件。

方案二呢則是時間至上的方案,可以做到快速遷移,但是不好的地方在于遷移出去的頁面其實還是塞在一個籃子里,只不過換了個新的籃子罷了。

方案三則是質量至上的方案,以時間作為成本,換來一個新的“低頻頁面項目”。具體要使用哪種方案,我覺得也是根據當前項目狀況而定,不追求最完美,追求最合適。

② 首屏加載

首屏加載,大概是優化的永恒話題,所有的優化都避不開這一個話題,因為只有它能最直觀的讓“大家”都感受到我們這次優化的成果。對于用戶來說,認為網頁首屏很快的標準其實很單一,就是一打開頁面,看了多久的白屏。所以我們需要做的就是弱化用戶對白屏的感知,圍繞這一點,個人認為,首屏加載這一優化可以有兩個方向:一個是速度,另一個是體驗。

引入加載占位

其實這個就是前段時間很火的“骨架屏”,我們可以在頁面真正被渲染出來之前,先給用戶看到一個“假的”頁面,等到某個時間節點(例如數據已經準備完畢...)就將真正的內容替換上去。這里有一個我寫的很不走心的例子:)

f6f1b8a490db1ae95938c21eef27fea9.gif

在這個優惠券列表頁面我的處理方案是,初始化頁面的時候就渲染3個列表項骨架,等待接口數據返回就將真實內容替換上去。

在我們的首屏其實也是類似的,我們可以根據首屏的展示結構,做一個匹配的骨架組件,然后按需求進行展示即可,這樣可以有效減少用戶看到白屏的時間。下面是我這個骨架的代碼,優化的空間很大,不過由于優先級不是很高,所以就沒有進行迭代了。

65d031b5d68c855adfa9dd5c3d183160.png

大概結構就是這樣,樣式方面很粗暴,因為每一項都是獨立的一個組件,直接可以用absolute定位堆砌一個簡潔的占位列表項。里面那個類似進度條的效果則是通過css3的animation實現的,我們可以將每個block的背景色變成漸變的,然后通過background-positon的變化來達到圖中的效果。

圖片懶加載

這應該是個老生常談的優化方向了,原理大概是將視圖之外的圖片都用同一個占位圖進行占位,將其真正的圖鏈接存在data-*中,通過監聽滾動來判斷圖片是否進入視圖中,來控制img標簽src的值。具體的實現很多地方都能搜到,大家可以根據自身情況,按需選擇。

不要將頁面都放到一個籃子里(二)-- 懶加載

其實在整個優化過程中我的重心是放在這個地方的,其他的都是半路上想到的...

讓我們回想一下,上面我們講過將低頻頁面分離,那么,必然就有會那么幾個訪問量十分高的頁面,那么對于這幾個頁面應該怎么辦呢?

因為訪問頻率高,所以我們可以認為這些頁面與我們的核心業務是強相關的,所以將其分離就顯得不那么劃算了(很可能會出現維護多套代碼的窘況)。

但是這樣高頻頁面才是優化的重點區域呀,應該怎么辦呢?面對這樣頁面我們還是可以使用懶加載大法(頁面懶加載 || 組件懶加載 || 依賴懶加載)。

想要在js層面實現各類懶加載,我們都需要借助webpack中的特性Code Splitting,它可以將我們本來打包在一起的js分解成一塊一塊,并能達到按需加載并使用的效果。

  • 頁面懶加載

因為我們使用了react-router,所以我們可以使用react-router的getComponent輕松達到頁面懶加載這一需求。如下圖所示,將mainpage這樣引入route的話,在打包的時候會將其分離成一個獨立的js。

43da81bb366ff5360c6da3278627a2ec.png
  • 組件懶加載 && 依賴懶加載

組件和依賴的懶加載也是十分簡單的,如下圖這樣寫就能達到懶加載的效果,但如果我們使用了babel則需要修改一下babel的配置,讓它能夠順利解析動態import()的語法。

51b4cb9e517240f8eeabcf803b4333e8.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的配置有基本的認知,緊接下來就是建目錄、裝依賴巴拉巴拉。最終我們會得到一個這樣的目錄結構--

fe6e3c445e842c636b1e34f767e4a103.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里)

b1ab9e7776bae008eddf0904c31c16e8.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: ''}])]
}

總結

好了,不知道有多少同學會看到這里,先謝謝大家看我在這嘮叨一堆~各類優化的方案在網上看了好多好多,但是好像大家都只講方案沒有涉及實踐,等到自己真正去玩的時候才發現,其實優化沒有想象中那么簡單,要兼顧原有的,又要盡量使用更新更好的,很多時候都會在夾縫中取舍。

其實,能夠優化的還有很多很多,請求方面、業務方面甚至是代碼寫法...都是可以優化的,但是這些怎么能一蹴而就呢?還是得走一步,看一步,選擇最適合自家項目的優化方案才是最佳方案~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/369998.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/369998.shtml
英文地址,請注明出處:http://en.pswp.cn/news/369998.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

搭建本地wordpress

1.首先,下載xampp,安裝按默認勾選即可。 2.安裝完成后,啟動Apache和MySQL這兩個服務。 啟動后變成綠色,表示啟動成功。 3.點擊MySQL項的Admin進入數據庫后臺。 4.點擊用戶賬戶新建用戶。 5.填寫用戶名,host name選本地…

編寫Java批注

Java 批注的允許的屬性類型刻意非常嚴格,但是允許的類型也可以使用一些簡潔的復合批注類型。 考慮教程站點中的示例注釋: package annotation; interface ClassPreamble {String author();String[] reviewers(); }在這里,作者和審閱者具有St…

Python基礎【day02】:字符串(四)

在Python中字符串本身有帶很多操作,字符串的特性,不可以被修改 0、字符串常用功能匯總 1、字符串的定義 #定義空字符串>>> name#定義非空字符串 >>> name"luoahong"#通過下標訪問 >>> name[1] u#不能修改字符串的值…

cryptojs vue 使用_VueJs里利用CryptoJs實現加密及解密

第一步 安裝安裝crypto-js第二步 創建在js文件目錄下創建一個js文件secret/*** 對頁面上輸入的密碼進行加密傳輸給后臺進行驗證,對返回的數據進行解密,在頁面展示*/let CryptoJS require(crypto-js); // 引入AES源碼jsexport default {/** 對密碼進行加…

html5 窗口變形,HTML5畫布(變形)

坐標變換案例1&#xff1a;function draw(){var cdocument.getElementById("myCanvas");var cxt c.getContext("2d");cxt.translate(200,50);cxt.fillStylergba(255,0,0,0.25);for(var i0;i<40;i){cxt.translate(25,25);cxt.scale(0.9,0.9);cxt.rotate(…

appium GUI介紹

Appium作為APP端的自動化測試工具&#xff0c;具有很多的有點&#xff0c;今天主要介紹一下它的UI界面&#xff0c;包含其中的一些參數等。主要說的是windows下的APPIUM GUI。 先看一眼它的界面(版本為1.4.16.1) 注: 1.android Settings - Android設置按鈕&#xff0c;所有和安…

迭代器模式和Java

大家好&#xff0c;在本文中&#xff0c;我們將檢查Iterator Pattern 。 我知道你們中許多人已經使用過一種設計模式&#xff0c;但是也許您沒有意識到它是模式&#xff0c;或者不知道它的巨大價值。 根據《 Head First Design 》一書&#xff1a; 迭代器模式提供了一種在不暴…

不使用JavaScript實現菜單的打開和關閉

我在寫有菜單欄的網頁時&#xff0c;基本都會用響應式設計來適配移動端&#xff0c;例如把不重要的菜單選項隱藏&#xff0c;或者創建一個菜單按鈕來控制的菜單的打開和關閉之類的。而我之前一直是使用JavaScript來實現菜單的打開和關閉的&#xff0c;但最近在網上看到有人使用…

負載均衡的幾種方式

&#xff08;1&#xff09;HTTP重定向負載均衡。 這種負載均衡方案的優點是比較簡單&#xff0c;缺點是瀏覽器需要每次請求兩次服務器才能拿完成一次訪問&#xff0c;性能較差。&#xff08;2&#xff09;DNS域名解析負載均衡。 DNS域名解析負載均衡的優點是將負載均衡工作交給…

芝枝.計算機與人文科學,計算機與人文科學

計算機與人文科學(2013-03-13 13:24:17)標簽&#xff1a;文化戰爭名著《靜靜的頓河》可以說從它誕生起便沒有平靜過&#xff0c;圍繞它的作者所引起的爭議&#xff0c;就像它獲得諾貝爾文學獎一樣&#xff0c;撼動文壇&#xff0c;有人指控肖洛霍夫是個騙子&#xff0c;《靜靜的…

rto初始化和計算_TCP系列13—重傳—3、協議中RTO計算和RTO定時器維護

從上一篇示例中我們可以看到在TCP中有一個重要的過程就是決定何時進行超時重傳&#xff0c;也就是RTO的計算更新。由于網絡狀況可能會受到路由變化、網絡負載等因素的影響&#xff0c;因此RTO也必須跟隨網絡狀況動態更新。如果TCP過早重傳&#xff0c;則可能會向網絡中注入很多…

在Java 8 Lambda上使用Apache Commons Functor功能接口

Apache Commons Functor &#xff08;以下稱為[functor]&#xff09;是一個Apache Commons組件&#xff0c;它提供功能性的編程API和已實現的幾種模式&#xff08;訪問者&#xff0c;生成器&#xff0c;聚合器等&#xff09;。 Java 8具有幾個不錯的新功能&#xff0c;包括lamb…

HTML5 Canvas游戲開發實戰 PDF掃描版

HTML5 Canvas游戲開發實戰主要講解使用HTML5 Canvas來開發和設計各類常見游戲的思路和技巧&#xff0c;在介紹HTML5 Canvas相關特性的同時&#xff0c;還通過游戲開發實例深入剖析了其內在原理&#xff0c;讓讀者不僅知其然&#xff0c;而且知其所以然。在本書中&#xff0c;除…

多線程之創建線程

在Java中&#xff0c;線程能區分兩種不同類型的線程&#xff1a;前臺線程和后臺線程。這兩者的區別就是&#xff1a;應用程序必須運行完所有的前臺線程才可以退出&#xff1b;而對于后臺線程&#xff0c;應用程序則可以不考慮其是否已經運行完畢而直接退出&#xff0c;所有的后…

阿 Q 的停車場

問題描述 剛拿到駕照的 KJ 總喜歡開著車到處兜風&#xff0c;玩完了再把車停到阿 Q 的停車場里&#xff0c;雖然 她對自己停車的水平很有信心&#xff0c;但她還是不放心其他人的停車水平&#xff0c;尤其是 Kelukin。于是&#xff0c; 她每次都把自己的愛車停在距離其它車最遠…

css3圖片垂直居中

圖片相對父元素垂直居中, css3屬性給父級元素設置 display: -webkit-box; -moz-box-align: center; -webkit-box-align: center; -moz-box-pack: center; -webkit-box-pack: center; 需要注意的是&#xff1a; 父級元素要有確定的高度&#xff01;

聲明式的理解【gpt】

一 MyBatis采用了聲明式語法來進行SQL映射配置【mybatis聲明式】 MyBatis是一款優秀的持久層框架&#xff0c;支持自定義SQL、存儲過程以及高級映射&#xff0c;使得開發人員能夠專注于SQL本身而不是數據庫訪問。MyBatis提供了兩種配置方式&#xff1a;XML配置和注解配置&…

網絡局域網看不到其它計算機,局域網中看不到其它計算機怎么辦

通過網上鄰居或查看網絡計算機時&#xff0c;看不到局域網中其它計算機&#xff0c;這是怎么回事呢?下面是學習啦小編給大家整理的一些有關看不到局域網中其它計算機的解決方法&#xff0c;希望對大家有幫助!局域網中看不到其它計算機的解決方法打開“控制面板”-“網絡和Inte…

iconfont 圖標轉為字體_iconfont字體圖標的使用方法--超簡單!

我之前因為項目用bootstrap比較多,所以使用font awesome字體圖標比較多,后來接觸到了iconfont,發現想要的什么圖標都有,還可以自定義圖標,非常強大!之前看了一波教程,覺得繁瑣,自己弄明白后感覺如此簡單,做了這么個簡單教程,直接上圖,簡單粗暴,避免新手走彎路,這里講解的默認是…

一罐來統治所有人

跳下內存通道 早在1998年&#xff0c;當我是一名C / C 開發人員時&#xff0c;嘗試使用Java時&#xff0c;有關該語言的一些內容對我來說就顯得有些惱火了。 我記得很擔心這些 為什么沒有合適的編輯器呢&#xff1f; C / C 有很多。 我為Java擁有的只是舊的記事本。 當我想要…