摘要:負(fù)載均衡就是用來幫助我們將眾多的客戶端請(qǐng)求合理的分配到各個(gè)服務(wù)器,以達(dá)到服務(wù)端資源的充分利用和更少的請(qǐng)求時(shí)間。如下面的配置復(fù)制代碼這樣可以完美繞過瀏覽器的同源策略訪問的屬于同源訪問,而對(duì)服務(wù)端轉(zhuǎn)發(fā)的請(qǐng)求不會(huì)觸發(fā)瀏覽器的同源策略。
性能優(yōu)化是一門大學(xué)問,本文僅對(duì)個(gè)人一些積累知識(shí)的闡述,歡迎下面補(bǔ)充。
拋出一個(gè)問題,從輸入url地址欄到所有內(nèi)容顯示到界面上做了哪些事?
1.瀏覽器向 DNS 服務(wù)器請(qǐng)求解析該 URL 中的域名所對(duì)應(yīng)的 IP 地址;
2.建立TCP連接(三次握手);
3.瀏覽器發(fā)出讀取文件(URL 中域名后面部分對(duì)應(yīng)的文件)的HTTP 請(qǐng)求,該請(qǐng)求報(bào)文作為 TCP 三次握手的第三個(gè)報(bào)文的數(shù)據(jù)發(fā)送給服務(wù)器;
4.服務(wù)器對(duì)瀏覽器請(qǐng)求作出響應(yīng),并把對(duì)應(yīng)的 html 文本發(fā)送給瀏覽器;
5.瀏覽器將該 html 文本并顯示內(nèi)容;
6.釋放 TCP連接(四次揮手);
上面這個(gè)問題是一個(gè)面試官非常喜歡問的問題,我們下面把這6個(gè)步驟分解,逐步細(xì)談優(yōu)化。一、DNS 解析
DNS`解析:將域名解析為ip地址 ,由上往下匹配,只要命中便停止
走緩存
瀏覽器DNS緩存
本機(jī)DNS緩存
路由器DNS緩存
網(wǎng)絡(luò)運(yùn)營商服務(wù)器DNS緩存 (80%的DNS解析在這完成的)
遞歸查詢
優(yōu)化策略:盡量允許使用瀏覽器的緩存,能給我們節(jié)省大量時(shí)間,下面有dns-prefetch的介紹,每次dns解析大概需要20-120秒二、TCP的三次握手
SYN (同步序列編號(hào))ACK(確認(rèn)字符)
第一次握手:Client將標(biāo)志位SYN置為1,隨機(jī)產(chǎn)生一個(gè)值seq=J,并將該數(shù)據(jù)包發(fā)送給Server,Client進(jìn)入SYN_SENT狀態(tài),等 待Server確認(rèn)。
第二次握手:Server收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道Client請(qǐng)求建立連接,Server將標(biāo)志位SYN和ACK都置為1,ack=J+1,隨機(jī)產(chǎn)生一個(gè)值seq=K,并將該數(shù)據(jù)包發(fā)送給Client以確認(rèn)連接請(qǐng)求,Server進(jìn)入SYN_RCVD狀態(tài)。
第三次握手:Client收到確認(rèn)后,檢查ack是否為J+1,ACK是否為1,如果正確則將標(biāo)志位ACK置為1,ack=K+1,并將該數(shù)據(jù)包發(fā)送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進(jìn)入ESTABLISHED狀態(tài),完成三次握手,隨后Client與Server之間可以開始傳輸數(shù)據(jù)了。
三、瀏覽器發(fā)送請(qǐng)求優(yōu)化策略:
1.HTTP協(xié)議通信最耗費(fèi)時(shí)間的是建立TCP連接的過程,那我們就可以使用HTTP Keep-Alive,在HTTP 早期,每個(gè)HTTP 請(qǐng)求都要求打開一個(gè)TCP socket連接,并且使用一次之后就斷開這個(gè)TCP連接。 使用keep-alive可以改善這種狀態(tài),即在一次TCP連接中可以持續(xù)發(fā)送多份數(shù)據(jù)而不會(huì)斷開連接。通過使用keep-alive機(jī)制,可以減少TCP連接建立次數(shù),也意味著可以減少TIME_WAIT狀態(tài)連接,以此提高性能和提高http服務(wù)器的吞吐率(更少的tcp連接意味著更少的系統(tǒng)內(nèi)核調(diào)用
2.但是,keep-alive并不是免費(fèi)的午餐,長時(shí)間的TCP連接容易導(dǎo)致系統(tǒng)資源無效占用。配置不當(dāng)?shù)?b>keep-alive,有時(shí)比重復(fù)利用連接帶來的損失還更大。所以,正確地設(shè)置keep-alive timeout時(shí)間非常重要。(這個(gè)keep-alive_timout時(shí)間值意味著:一個(gè)http產(chǎn)生的tcp連接在傳送完最后一個(gè)響應(yīng)后,還需要hold住keepalive_timeout秒后,才開始關(guān)閉這個(gè)連接),如果想更詳細(xì)了解可以看這篇文章keep-alve性能優(yōu)化的測(cè)試結(jié)果
3.使用webScoket通信協(xié)議,僅一次TCP握手就一直保持連接,而且他對(duì)二進(jìn)制數(shù)據(jù)的傳輸有更好的支持,可以應(yīng)用于即時(shí)通信,海量高并發(fā)場(chǎng)景。webSocket的原理以及詳解
4.減少HTTP請(qǐng)求次數(shù),每次HTTP請(qǐng)求都會(huì)有請(qǐng)求頭,返回響應(yīng)都會(huì)有響應(yīng)頭,多次請(qǐng)求不僅浪費(fèi)時(shí)間而且會(huì)讓網(wǎng)絡(luò)傳輸很多無效的資源,使用前端模塊化技術(shù) AMD CMD commonJS ES6等模塊化方案將多個(gè)文件壓縮打包成一個(gè),當(dāng)然也不能都放在一個(gè)文件中,因?yàn)檫@樣傳輸起來可能會(huì)很慢,權(quán)衡取一個(gè)中間值
5.配置使用懶加載,對(duì)于一些用戶不立刻使用到的文件到特定的事件觸發(fā)再請(qǐng)求,也許用戶只是想看到你首頁上半屏的內(nèi)容,但是你卻請(qǐng)求了整個(gè)頁面的所有圖片,如果用戶量很大,那么這是一種極大的浪費(fèi)
6.服務(wù)器資源的部署盡量使用同源策略
7.在需要多個(gè)cookie去辨識(shí)用戶的多種狀況時(shí),使用session替代,把數(shù)據(jù)儲(chǔ)存在服務(wù)器端或者服務(wù)器端的數(shù)據(jù)庫中,這樣只需要一個(gè)cookie傳輸,節(jié)省大量的無效傳輸,而且儲(chǔ)存的數(shù)據(jù)可以是永久無線大的。
8.使用preload和dns-prefetch、prefetch,預(yù)請(qǐng)求資源,這種請(qǐng)求方式不會(huì)阻塞瀏覽器的解析,而且能將預(yù)請(qǐng)求的資源緩存起來,而且可以設(shè)置crossorgin進(jìn)行跨域資源的緩存,不會(huì)推遲首屏的渲染時(shí)間,還會(huì)加快后面的加載時(shí)間,因?yàn)楹竺娴谋旧硇枰馁Y源會(huì)直接從緩存中讀取,而不會(huì)走網(wǎng)絡(luò)請(qǐng)求。
9.使用defer和async屬性的腳本,異步加載的方式,會(huì)先發(fā)請(qǐng)求,然后JS引擎繼續(xù)解析下面的內(nèi)容。async的屬性腳本會(huì)無序加載,誰先請(qǐng)求回來就立刻加載誰,當(dāng)請(qǐng)求回來的時(shí)候,無論是在DOM解析還是腳本的解析,接下來都先會(huì)解析這個(gè)asncy腳本,它會(huì)阻塞DOM的解析。defer屬性的會(huì)按HTML結(jié)構(gòu)的按順序加載,在DOMContentLoad前加載,但是加載之前所有的DOM解析肯定已經(jīng)完成了,defer屬性的腳本不會(huì)阻塞DOM的解析,它也叫延遲腳本。由于實(shí)際中它不確定是否在DOMContentLoaded前加載,所以一般只放一個(gè)defer的腳本,參考移動(dòng)端京東網(wǎng)頁。 async和defer詳解
詳情參考preload和prefetch詳解
四、服務(wù)器返回響應(yīng),瀏覽器接受到響應(yīng)數(shù)據(jù)一直沒想到這里使用什么優(yōu)化手段,今晚想到了,使用Nginx反向代理服務(wù)器,主要是對(duì)服務(wù)器端的優(yōu)化。
Nginx是一款輕量級(jí)的Web 服務(wù)器/反向代理服務(wù)器及電子郵件(IMAP/POP3)代理服務(wù)器,并在一個(gè)BSD-like 協(xié)議下發(fā)行。其特點(diǎn)是占有內(nèi)存少,并發(fā)能力強(qiáng),事實(shí)上nginx的并發(fā)能力確實(shí)在同類型的網(wǎng)頁服務(wù)器中表現(xiàn)較好,中國大陸使用nginx網(wǎng)站用戶有:百度、京東、新浪、網(wǎng)易、騰訊、淘寶等。
Nginx 是一個(gè)安裝非常的簡(jiǎn)單、配置文件非常簡(jiǎn)潔(還能夠支持perl語法)、Bug非常少的服務(wù)。Nginx 啟動(dòng)特別容易,并且?guī)缀蹩梢宰龅?*24不間斷運(yùn)行,即使運(yùn)行數(shù)個(gè)月也不需要重新啟動(dòng)。你還能夠不間斷服務(wù)的情況下進(jìn)行軟件版本的升級(jí)。
它可以:解決跨域,請(qǐng)求過濾,配置gzip,負(fù)載均衡,靜態(tài)資源服務(wù)器 等...
把服務(wù)窗口想像成我們的后端服務(wù)器,而后面終端的人則是無數(shù)個(gè)客戶端正在發(fā)起請(qǐng)求。負(fù)載均衡就是用來幫助我們將眾多的客戶端請(qǐng)求合理的分配到各個(gè)服務(wù)器,以達(dá)到服務(wù)端資源的充分利用和更少的請(qǐng)求時(shí)間。
Nginx如何實(shí)現(xiàn)負(fù)載均衡
nginx如何實(shí)現(xiàn)負(fù)載均衡
Upstream指定后端服務(wù)器地址列表 upstream balanceServer { server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345; } 復(fù)制代碼在server中攔截響應(yīng)請(qǐng)求,并將請(qǐng)求轉(zhuǎn)發(fā)到Upstream中配置的服務(wù)器列表。 server { server_name fe.server.com; listen 80; location /api { proxy_pass http://balanceServer; } }
上面的配置只是指定了nginx需要轉(zhuǎn)發(fā)的服務(wù)端列表,并沒有指定分配策略。
默認(rèn)情況下采用的策略,將所有客戶端請(qǐng)求輪詢分配給服務(wù)端。這種策略是可以正常工作的,但是如果其中某一臺(tái)服務(wù)器壓力太大,出現(xiàn)延遲,會(huì)影響所有分配在這臺(tái)服務(wù)器下的用戶。
最小連接數(shù)策略
將請(qǐng)求優(yōu)先分配給壓力較小的服務(wù)器,它可以平衡每個(gè)隊(duì)列的長度,并避免向壓力大的服務(wù)器添加更多的請(qǐng)求。
upstream balanceServer { least_conn; //配置壓力較小的服務(wù)器 server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345; }
依賴于NGINX Plus,優(yōu)先分配給響應(yīng)時(shí)間最短的服務(wù)器。
upstream balanceServer { fair; //配置響應(yīng)時(shí)間最短的服務(wù)器 server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345; }
客戶端ip綁定
來自同一個(gè)ip的請(qǐng)求永遠(yuǎn)只分配一臺(tái)服務(wù)器,有效解決了動(dòng)態(tài)網(wǎng)頁存在的session共享問題。 upstream balanceServer { ip_hash; //配置1個(gè)IP永遠(yuǎn)只分配一臺(tái)服務(wù)器 server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345; }
配置靜態(tài)資源服務(wù)器
location ~* .(png|gif|jpg|jpeg)$ { root /root/static/; autoindex on; access_log off; expires 10h;# 設(shè)置過期時(shí)間為10小時(shí) } 復(fù)制代碼匹配以png|gif|jpg|jpeg為結(jié)尾的請(qǐng)求, 并將請(qǐng)求轉(zhuǎn)發(fā)到本地路徑,root中指定的路徑即nginx 本地路徑。同時(shí)也可以進(jìn)行一些緩存的設(shè)置。
Nginx解決跨域
nginx解決跨域的原理 例如: 前端server的域名為:fe.server.com 后端服務(wù)的域名為:dev.server.com 現(xiàn)在我在fe.server.com對(duì)dev.server.com發(fā)起請(qǐng)求一定會(huì)出現(xiàn)跨域。 現(xiàn)在我們只需要啟動(dòng)一個(gè)nginx服務(wù)器,將server_name設(shè)置為fe.server.com, 然后設(shè)置相應(yīng)的location以攔截前端需要跨域的請(qǐng)求,最后將請(qǐng)求代理回dev.server.com。 如下面的配置: server { listen 80; server_name fe.server.com; location / { proxy_pass dev.server.com; } } 復(fù)制代碼這樣可以完美繞過瀏覽器的同源策略:fe.server.com訪問nginx的fe.server.com 屬于同源訪問,而nginx對(duì)服務(wù)端轉(zhuǎn)發(fā)的請(qǐng)求不會(huì)觸發(fā)瀏覽器的同源策略。
最重要的一點(diǎn)來了,現(xiàn)在的BATJ大都使用了這種配置:
配置GZIP
GZIP是規(guī)定的三種標(biāo)準(zhǔn)HTTP壓縮格式之一。目前絕大多數(shù)的網(wǎng)站都在使用GZIP傳輸 HTML、CSS、JavaScript 等資源文件。
對(duì)于文本文件,GZip 的效果非常明顯,開啟后傳輸所需流量大約會(huì)降至 1/4 ~ 1/3。
啟用 GZip 所需的HTTP 最低版本默認(rèn)值為HTTP/1.1
啟用gzip同時(shí)需要客戶端和服務(wù)端的支持,如果客戶端支持gzip的解析,那么只要服務(wù)端能夠返回gzip的文件就可以啟用gzip了,我們可以通過nginx的配置來讓服務(wù)端支持gzip。下面的respone中content-encoding:gzip,指服務(wù)端開啟了gzip的壓縮方式。
具體可以看這篇文字文章 Nginx配置GZIP
對(duì)于文本文件,GZip 的效果非常明顯,開啟后傳輸所需流量大約會(huì)降至 1/4 ~ 1/3。
Nginx功能非常強(qiáng)大,配置也非常方便,有興趣的可以多看看這篇文章 Nginx解析五、瀏覽器解析數(shù)據(jù),繪制渲染頁面的過程
先預(yù)解析(將需要發(fā)送請(qǐng)求的標(biāo)簽的請(qǐng)求發(fā)出去)
從上到下解析html文件
遇到HTML標(biāo)簽,調(diào)用html解析器將其解析DOM樹
遇到css標(biāo)記,調(diào)用css解析器將其解析CSSOM樹
link 阻塞 - 為了解決閃屏,所有解決閃屏的樣式
style 非阻塞,與閃屏的樣式不相關(guān)的
將DOM樹和CSSOM樹結(jié)合在一起,形成render樹
layout布局 render渲染
遇到script標(biāo)簽,阻塞,調(diào)用js解析器解析js代碼,可能會(huì)修改DOM樹,也可能會(huì)修改CSSOM樹
將DOM樹和CSSOM樹結(jié)合在一起,形成render樹
layout布局 render渲染(重排重繪)
script標(biāo)簽的屬性 asnyc defer
性能優(yōu)化策略:
需要阻塞的樣式使用link引入,不需要的使用style標(biāo)簽(具體是否需要阻塞看業(yè)務(wù)場(chǎng)景)
圖片比較多的時(shí)候,一定要使用懶加載,圖片是最需要優(yōu)化的,webpack4中也要配置圖片壓縮,能極大壓縮圖片大小,對(duì)于新版本瀏覽器可以使用webp格式圖片webP詳解,圖片優(yōu)化對(duì)性能提升最大。
webpack4配置 代碼分割,提取公共代碼成多帶帶模塊。方便緩存
/* runtimeChunk 設(shè)置為 true, webpack 就會(huì)把 chunk 文件名全部存到一個(gè)多帶帶的 chunk 中, 這樣更新一個(gè)文件只會(huì)影響到它所在的 chunk 和 runtimeChunk,避免了引用這個(gè) chunk 的文件也發(fā)生改變。 */ runtimeChunk: true, splitChunks: { chunks: "all" // 默認(rèn) entry 的 chunk 不會(huì)被拆分, 配置成 all, 就可以了 } } //因?yàn)槭菃稳肟谖募渲茫詻]有考慮多入口的情況,多入口是應(yīng)該分別進(jìn)行處理。
對(duì)于需要事件驅(qū)動(dòng)的webpack4配置懶加載的,可以看這篇webpack4優(yōu)化教程,寫得非常全面
一些原生javaScript的DOM操作等優(yōu)化會(huì)在下面總結(jié)
六、TCP的四次揮手,斷開連接 終結(jié)篇:性能只是 load 時(shí)間或者 DOMContentLoaded 時(shí)間的問題嗎?
RAIL
Responce 響應(yīng),研究表明,100ms內(nèi)對(duì)用戶的輸入操作進(jìn)行響應(yīng),通常會(huì)被人類認(rèn)為是立即響應(yīng)。時(shí)間再長,操作與反應(yīng)之間的連接就會(huì)中斷,人們就會(huì)覺得它的操作有延遲。例如:當(dāng)用戶點(diǎn)擊一個(gè)按鈕,如果100ms內(nèi)給出響應(yīng),那么用戶就會(huì)覺得響應(yīng)很及時(shí),不會(huì)察覺到絲毫延遲感。
Animaton 現(xiàn)如今大多數(shù)設(shè)備的屏幕刷新頻率是60Hz,也就是每秒鐘屏幕刷新60次;因此網(wǎng)頁動(dòng)畫的運(yùn)行速度只要達(dá)到60FPS,我們就會(huì)覺得動(dòng)畫很流暢。
Idle RAIL規(guī)定,空閑周期內(nèi)運(yùn)行的任務(wù)不得超過50ms,當(dāng)然不止RAIL規(guī)定,W3C性能工作組的Longtasks標(biāo)準(zhǔn)也規(guī)定了超過50毫秒的任務(wù)屬于長任務(wù),那么50ms這個(gè)數(shù)字是怎么得來的呢?瀏覽器是單線程的,這意味著同一時(shí)間主線程只能處理一個(gè)任務(wù),如果一個(gè)任務(wù)執(zhí)行時(shí)間過長,瀏覽器則無法執(zhí)行其他任務(wù),用戶會(huì)感覺到瀏覽器被卡死了,因?yàn)樗妮斎氲貌坏饺魏雾憫?yīng)。為了達(dá)到100ms內(nèi)給出響應(yīng),將空閑周期執(zhí)行的任務(wù)限制為50ms意味著,即使用戶的輸入行為發(fā)生在空閑任務(wù)剛開始執(zhí)行,瀏覽器仍有剩余的50ms時(shí)間用來響應(yīng)用戶輸入,而不會(huì)產(chǎn)生用戶可察覺的延遲。
Load如果不能在1秒鐘內(nèi)加載網(wǎng)頁并讓用戶看到內(nèi)容,用戶的注意力就會(huì)分散。用戶會(huì)覺得他要做的事情被打斷,如果10秒鐘還打不開網(wǎng)頁,用戶會(huì)感到失望,會(huì)放棄他們想做的事,以后他們或許都不會(huì)再回來。
如何使網(wǎng)頁更絲滑?
使用requestAnimationFrame
即便你能保證每一幀的總耗時(shí)都小于16ms,也無法保證一定不會(huì)出現(xiàn)丟幀的情況,這取決于觸發(fā)JS執(zhí)行的方式。假設(shè)使用 setTimeout 或 setInterval 來觸發(fā)JS執(zhí)行并修改樣式從而導(dǎo)致視覺變化;那么會(huì)有這樣一種情況,因?yàn)閟etTimeout 或 setInterval沒有辦法保證回調(diào)函數(shù)什么時(shí)候執(zhí)行,它可能在每一幀的中間執(zhí)行,也可能在每一幀的最后執(zhí)行。所以會(huì)導(dǎo)致即便我們能保障每一幀的總耗時(shí)小于16ms,但是執(zhí)行的時(shí)機(jī)如果在每一幀的中間或最后,最后的結(jié)果依然是沒有辦法每隔16ms讓屏幕產(chǎn)生一次變化,也就是說,即便我們能保證每一幀總體時(shí)間小于16ms,但如果使用定時(shí)器觸發(fā)動(dòng)畫,那么由于定時(shí)器的觸發(fā)時(shí)機(jī)不確定,所以還是會(huì)導(dǎo)致動(dòng)畫丟幀。現(xiàn)在整個(gè)Web只有一個(gè)API可以解決這個(gè)問題,那就是requestAnimationFrame,它可以保證回調(diào)函數(shù)穩(wěn)定的在每一幀最開始觸發(fā)。
避免FSL
先執(zhí)行JS,然后在JS中修改了樣式從而導(dǎo)致樣式計(jì)算,然后樣式的改動(dòng)觸發(fā)了布局、繪制、合成。但JavaScript可以強(qiáng)制瀏覽器將布局提前執(zhí)行,這就叫 強(qiáng)制同步布局FSL。
//讀取offsetWidth的值會(huì)導(dǎo)致重繪 const newWidth = container.offsetWidth; //設(shè)置width的值會(huì)導(dǎo)致重排,但是for循環(huán)內(nèi)部 代碼執(zhí)行速度極快,當(dāng)上面的查詢操作導(dǎo)致的重繪 還沒有完成,下面的代碼又會(huì)導(dǎo)致重排,而且這個(gè)重 排會(huì)強(qiáng)制結(jié)束上面的重繪,直接重排,這樣對(duì)性能影響 非常大。所以我們一般會(huì)在循環(huán)外部定義一個(gè)變量,這里 面使用變量代替container.offsetWidth; boxes[i].style.width = newWidth + "px"; }
使用transform屬性去操作動(dòng)畫,這個(gè)屬性是由合成器多帶帶處理的,所以使用這個(gè)屬性可以避免布局與繪制。
使用translateZ(0)開啟圖層,減少重繪重排。特別在移動(dòng)端,盡量使用transform代替absolute。創(chuàng)建圖層的最佳方式是使用will-change,但某些不支持這個(gè)屬性的瀏覽器可以使用3D 變形(transform: translateZ(0))來強(qiáng)制創(chuàng)建一個(gè)新層。
有興趣的可以看看這篇文字 前端頁面優(yōu)化
樣式的切換最好提前定義好class,通過class的切換批量修改樣式,避免多次重繪重排
可以先切換display:none再修改樣式
多次的append 操作可以先插入到一個(gè)新生成的元素中,再一次性插入到頁面中。
代碼復(fù)用,函數(shù)柯里化,封裝高階函數(shù),將多次復(fù)用代碼封裝成普通函數(shù)(俗稱方法),React中封裝成高階組件,ES6中可以使用繼承,TypeScript中接口繼承,類繼承,接口合并,類合并。
在把數(shù)據(jù)儲(chǔ)存在localstorage和sessionstorage中時(shí),可以再自己定義一個(gè)模塊,把這些數(shù)據(jù)在內(nèi)存中存儲(chǔ)一份,這樣只要可以直接從內(nèi)存中讀書,速度更快,性能更好。
能不定義全局變量就不定義全局變量,最好使用局部變量代替全局變量,查找的速度要高一倍。
強(qiáng)力推薦閱讀:阮一峰ES6教程
以及什么是TypeScript以及入門
下面加入React的性能優(yōu)化方案:
在生命周期函數(shù)shouldComponentUpdate中對(duì)this.state和prev state進(jìn)行淺比較,使用for-in循環(huán)遍歷兩者,
只要得到他們每一項(xiàng)值,只要有一個(gè)不一樣就返回true,更新組件。
定義組件時(shí)不適用React.component , 使用PureComponent代替,這樣React機(jī)制會(huì)自動(dòng)在shouldComponentUpdate中進(jìn)行淺比較,決定是否更新。
上面兩條優(yōu)化方案只進(jìn)行淺比較,只對(duì)比直接屬性的值,當(dāng)然你還可以在上面加入this.props和prevprops的遍歷比較,因?yàn)?b>shouldComponentUpdate的生命周期函數(shù)自帶這兩個(gè)參數(shù)。如果props 和 state 的值比較復(fù)雜,那么可以使用下面這種方式去進(jìn)行深比較。
解決:
保證每次都是新的值
使用 immutable-js 庫,這個(gè)庫保證生成的值都是唯一的
var map1 = Immutable.Map({ a: 1, b: 2, c: 3 }); var map2 = map1.set("b", 50); map1.get("b"); // 2 map2.get("b"); // 50
總結(jié):使用以上方式,可以減少不必要的重復(fù)渲染。
React的JSX語法要求必須包裹一層根標(biāo)簽,為了減少不必要的DOM層級(jí),我們使用Fragment標(biāo)簽代替,這樣渲染時(shí)候不會(huì)渲染多余的DOM節(jié)點(diǎn),讓DIFF算法更快遍歷。
使用Redux管理全局多個(gè)組件復(fù)用的狀態(tài)。
React構(gòu)建的是SPA應(yīng)用,對(duì)SEO不夠友好,可以選擇部分SSR技術(shù)進(jìn)行SEO優(yōu)化。
對(duì)Ant-design這類的UI組件庫,進(jìn)行按需加載配置,從import Button from "antd" 的引入方式,變成import {Button} from antd的方式引入。(類似Babel7中的runtime和polifill的區(qū)別).
在React中一些數(shù)據(jù)的需要更新,但是卻不急著使用,或者說每次更新的這個(gè)數(shù)據(jù)不需要更新組件重新渲染的,可以定期成類的實(shí)例上的屬性,這樣能減少多次的重復(fù)無意義的DIFF和渲染。
Redux的使用要看情況使用,如果只是一個(gè)局部狀態(tài)(僅僅是一個(gè)組件或者父子組件使用就不要使用Redux)。對(duì)于一個(gè)父子、父子孫多層組件需要用到的state數(shù)據(jù),也可以使用context上下文去傳遞. Context上下文詳解,但是復(fù)雜項(xiàng)目的多個(gè)不同層次組件使用到的state,必須上Redux。
所有的原生監(jiān)聽事件,定時(shí)器等,必須在componentWillUnmount中清除,否則大型項(xiàng)目必定會(huì)發(fā)生內(nèi)存泄露,極度影響性能!!!
React Hooks是什么?
用來定義有狀態(tài)和生命周期函數(shù)的純函數(shù)組件(在過去純函數(shù)組件是沒有狀態(tài)和生命周期函數(shù)的~)
Hooks是React v16.7.0-alpha中加入的新特性,并向后兼容。
什么是鉤子(Hook)本質(zhì)就是函數(shù),能讓你使用React組件的狀態(tài)和生命周期函數(shù)
讓代碼更加可復(fù)用,不用在定義繁雜的HOC(高階組件)和class組件。
使用:
useState(initValue) - const [ state, setState ] = React.useState(initValue); - 用來定義狀態(tài)數(shù)據(jù)和操作狀態(tài)數(shù)據(jù)的方法 useEffect(function) - useEffect(() => { do something }) - 副作用函數(shù)(發(fā)請(qǐng)求獲取數(shù)據(jù)、訂閱事件、修改DOM等) - 本質(zhì)上就是一個(gè)生命周期函數(shù),相當(dāng)于componentDidMount 、 componentDidUpdate 和 componentWillUnmount useContext(Context) - context指的是React.createContext返回值 ------ 以下Hooks只使用于特殊場(chǎng)景,需要時(shí)在用 ----- useReducer - const [state, dispatch] = useReducer(reducer, initialState); - 一個(gè) useState 替代方案,相當(dāng)于redux useCallback - useCallback(fn, inputs) - 相當(dāng)于 shouldComponentUpdate,只有inputs的值發(fā)生變化才會(huì)調(diào)用fn useMemo(create, inputs) - 相當(dāng)于useCallback
更多詳見官方文檔:HOOKS文檔
注意
只能在頂層調(diào)用鉤子。不要在循環(huán),控制流和嵌套的函數(shù)中調(diào)用鉤子。
只能從React的函數(shù)式組件中調(diào)用鉤子。不要在常規(guī)的JavaScript函數(shù)中調(diào)用鉤子。-(此外,你也可以在你的自定義鉤子中調(diào)用鉤子。)
原生JavaScript實(shí)現(xiàn)懶加載:
懶加載,從字面意思就可以簡(jiǎn)單的理解為不到用時(shí)就不去加載,對(duì)于頁面中的元素,我們可以這樣理解:只有當(dāng)滾動(dòng)頁面內(nèi)容使得本元素進(jìn)入到瀏覽器視窗時(shí)(或者稍微提前,需給定提前量),我們才開始加載圖片;
不給img元素的src屬性賦值時(shí),不會(huì)發(fā)出請(qǐng)求【不能使src="",這樣即使只給src賦了空值也會(huì)發(fā)出請(qǐng)求】,而一旦給src屬性賦予資源地址值,那么該請(qǐng)求發(fā)出,使得圖片顯示;所以這里我們利用這一點(diǎn)控制img元素的加載時(shí)機(jī)。在開始的時(shí)候?qū)①Y源url放置在自定義屬性data-src當(dāng)中,然后在需要加載的時(shí)候獲取該屬性并賦值給元素的src屬性
從上面的分析可以看出來,主要要解決的問題就是怎么檢測(cè)到元素是否在視窗當(dāng)中,這里我們要借助于dom操作api當(dāng)中的el.getBoundingClientRect()來獲取其位置,并判斷是否在視窗內(nèi),這里簡(jiǎn)單描述。
Element.getBoundingClientRect()方法返回元素的大小及其相對(duì)于視口的位置。返回值是一個(gè) DOMRect 對(duì)象,這個(gè)對(duì)象是由該元素的 getClientRects() 方法返回的一組矩形的集合, 即:是與該元素相關(guān)的CSS 邊框集合 。DOMRect 對(duì)象包含了一組用于描述邊框的只讀屬性——left、top、right和bottom,單位為像素。除了 width 和 height 外的屬性都是相對(duì)于視口的左上角位置而言的。
因此我們可以使用以下邏輯判斷元素是否進(jìn)入視窗:
function isInSight(el){ var eldom = typeof el == "object"?el:document.querySelector(el); var bound = eldom.getBoundingClientRect(); // 這里的bound包含了el距離視窗的距離; // bound.left是元素距離窗口左側(cè)的距離值; // bound.top是袁術(shù)距離窗口頂端的距離值; // 以以上兩個(gè)數(shù)值判斷元素是否進(jìn)入視窗; var clientHeigt = window.innerHeight; var clientWidth = window.innerWidth; // return (bound.top>=0&&bound.left>=0)&&(bound.top<=window.innerHeight+20)&&(bound.left<=window.innerWidth+20); return !((bound.top>clientHeigt)||(bound.bottom<0)||(bound.left>clientWidth)||(bound.right<0)) }
其中window.innerHeight和window.innerWidth分別為視窗的高度和寬度,之所以加上20是為了讓懶加載稍稍提前,使用戶體驗(yàn)更好;
添加scroll事件監(jiān)聽:
那么什么時(shí)候去檢測(cè)元素是否在視窗內(nèi),并判斷是否加載呢,這里由于頁面的滾動(dòng)會(huì)使得元素相對(duì)于視窗的位置發(fā)生變化,也就是說滾動(dòng)會(huì)改變isInSight的結(jié)果,所以這里我們?cè)趙indow上添加scroll事件監(jiān)聽:
// 當(dāng)加載完成,檢測(cè)并加載可視范圍內(nèi)的圖片 window.onload= checkAllImgs; // 添加滾動(dòng)監(jiān)聽,即可視范圍變化時(shí)檢測(cè)當(dāng)前范圍內(nèi)的圖片是否可以加載了 window.addEventListener("scroll",function(){ checkAllImgs(); }) // 檢測(cè)所有圖片,并給視窗中的圖片的src屬性賦值,即開始加載; function checkAllImgs(){ var imgs = document.querySelectorAll("img"); Array.prototype.forEach.call(imgs,function(el){ if(isInSight(el)){ loadImg(el); } }) } // 開始加載指定el的資源 function loadImg(el){ var eldom = typeof el == "object"?el:document.querySelector(el); if(!eldom.src){ // 懶加載img定義如: var source = eldom.getAttribute("data-src"); var index = eldom.getAttribute("data-index"); eldom.src = source; console.log("第"+index+"張圖片進(jìn)入視窗,開始加載。。。。") } }
這樣就實(shí)現(xiàn)了圖片的懶加載的簡(jiǎn)單實(shí)現(xiàn),當(dāng)然還可以對(duì)scroll進(jìn)行優(yōu)化等操作。
現(xiàn)在最新版本的谷歌瀏覽器也要支持 標(biāo)簽的內(nèi)部 loading屬性了,相信未來開發(fā)會(huì)越來越方便。
以上都是根據(jù)本人的知識(shí)點(diǎn)總結(jié)得出,后期還會(huì)有更多性能優(yōu)化方案等出來,路過點(diǎn)個(gè)贊收藏收藏~,歡迎提出問題補(bǔ)充~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/114414.html
摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...
摘要:負(fù)載均衡就是用來幫助我們將眾多的客戶端請(qǐng)求合理的分配到各個(gè)服務(wù)器,以達(dá)到服務(wù)端資源的充分利用和更少的請(qǐng)求時(shí)間。如下面的配置復(fù)制代碼這樣可以完美繞過瀏覽器的同源策略訪問的屬于同源訪問,而對(duì)服務(wù)端轉(zhuǎn)發(fā)的請(qǐng)求不會(huì)觸發(fā)瀏覽器的同源策略。 性能優(yōu)化是一門大學(xué)問,本文僅對(duì)個(gè)人一些積累知識(shí)的闡述,歡迎下面補(bǔ)充。 拋出一個(gè)問題,從輸入url地址欄到所有內(nèi)容顯示到界面上做了哪些事? 1.瀏覽器向 DN...
摘要:安裝后已經(jīng)完成了安裝,并且等待其他的線程被關(guān)閉。激活后在這個(gè)狀態(tài)會(huì)處理事件回調(diào)提供了更新緩存策略的機(jī)會(huì)。并可以處理功能性的事件請(qǐng)求后臺(tái)同步推送。廢棄狀態(tài)這個(gè)狀態(tài)表示一個(gè)的生命周期結(jié)束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺,已經(jīng)來到了最后的下篇 其實(shí)我寫的東西你如果認(rèn)真去看,跟著去寫,應(yīng)該能有...
摘要:前端每周清單第期與模式變遷與優(yōu)化界面生成作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000013279448); 前端每周清單第 51 期: React Context A...
閱讀 3256·2021-10-27 14:20
閱讀 2530·2021-10-08 10:05
閱讀 1634·2021-09-09 09:33
閱讀 2906·2019-08-30 13:16
閱讀 1442·2019-08-29 18:34
閱讀 1176·2019-08-29 10:58
閱讀 1231·2019-08-28 18:22
閱讀 1229·2019-08-26 13:33