從前端性能優(yōu)化引申出來(lái)的經(jīng)典面試題有哪些

本篇內(nèi)容介紹了“從前端性能優(yōu)化引申出來(lái)的經(jīng)典面試題有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

柴桑ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!

渲染優(yōu)化

渲染優(yōu)化是前端優(yōu)化中一個(gè)很重要的部分,一個(gè)好的首屏?xí)r間能給用戶帶來(lái)很好的體驗(yàn),這里要說(shuō)的一點(diǎn)是關(guān)于首屏?xí)r間的定義,不同的團(tuán)隊(duì)對(duì)首屏?xí)r間定義不一樣,有的團(tuán)隊(duì)認(rèn)為首屏?xí)r間就是白屏?xí)r間,是從頁(yè)面加載到第一個(gè)畫(huà)面出現(xiàn)的時(shí)間。但是當(dāng)我們說(shuō)到用戶體驗(yàn)的時(shí)候,僅僅是這樣還達(dá)不到效果,所以有的前端團(tuán)隊(duì)認(rèn)為,首屏?xí)r間應(yīng)該是從頁(yè)面加載到用戶可以進(jìn)行正常的頁(yè)面操作時(shí)間,那么我們就依照后者來(lái)進(jìn)行說(shuō)明

js css 加載順序

說(shuō)渲染優(yōu)化之前,我們還需要說(shuō)一個(gè)小插曲,就是比較經(jīng)典的一道問(wèn)題"瀏覽器地址欄輸入url發(fā)生了什么",理解了這個(gè)我們才可以更清楚js,css加載順序?qū)︿秩镜挠绊?/p>

問(wèn)題 1:地址欄輸入url 發(fā)生了什么

這個(gè)問(wèn)題經(jīng)常被人提起,有人回答比較簡(jiǎn)潔點(diǎn),有人可能回答的比較詳細(xì),下面就說(shuō)一下主要流程

  •  首先會(huì)進(jìn)行 url 解析,根據(jù) DNS 系統(tǒng)進(jìn)行 ip 查找

  •  根據(jù) ip 就可以找到服務(wù)器,然后瀏覽器和服務(wù)器會(huì)進(jìn)行 TCP 三次握手建立連接,如果此時(shí)是 https 的話,還會(huì)建立 TLS 連接以及協(xié)商加密算法,這里就會(huì)出現(xiàn)另一個(gè)需要注意的問(wèn)題"https 和 http 的區(qū)別"(下文會(huì)講到)

  •     連接建立之后瀏覽器開(kāi)始發(fā)送請(qǐng)求獲取文件,此時(shí)這里還會(huì)出現(xiàn)一種情況就是緩存,建立連接后是走緩存還是直接重新獲取,需要看后臺(tái)設(shè)置,所以這里會(huì)有一個(gè)關(guān)注的問(wèn)題"瀏覽器緩存機(jī)制",緩存我們等會(huì)在講,現(xiàn)在我們就當(dāng)沒(méi)有緩存,直接去獲取文件

  •  首先獲取 html 文件,構(gòu)建 DOM 樹(shù),這個(gè)過(guò)程是邊下載邊解析,并不是等 html 文件全部下載完了,再去解析 html,這樣比較浪費(fèi)時(shí)間,而是下載一點(diǎn)解析一點(diǎn)

  •  好了解析到 html 頭部時(shí)候,又會(huì)出現(xiàn)一種問(wèn)題,css,js 放到哪里了?不同的位置會(huì)造成渲染的不同,此時(shí)就會(huì)出現(xiàn)另一個(gè)需要關(guān)注的問(wèn)題"css,js 位置應(yīng)該放哪里?為什么",我們先按照正確的位置來(lái)說(shuō)明(css 放頭部,js 放尾部)

  •  解析到了 html 頭部發(fā)現(xiàn)有 css 文件,此時(shí)下載 css 文件,css 文件也是一邊下載一邊解析的,構(gòu)建的是 CSSOM 樹(shù),當(dāng) DOM 樹(shù)和 CSSOM 樹(shù)全部構(gòu)建完之后,瀏覽器會(huì)把 DOM 樹(shù)和 CSSOM 樹(shù)構(gòu)建成渲染樹(shù)。

  •  樣式計(jì)算, 上面最后一句"DOM 樹(shù)和 CSSOM 樹(shù)會(huì)一起構(gòu)建成渲染樹(shù)"說(shuō)的有點(diǎn)籠統(tǒng),其實(shí)還有更細(xì)一點(diǎn)的操作,但是一般回答到上面應(yīng)該就可以了,我們現(xiàn)在接上面說(shuō)一下構(gòu)造渲染樹(shù)的時(shí)候還做了哪些事情。第一個(gè)就是樣式計(jì)算,DOM樹(shù) 和 CSSOM樹(shù)有了之后,瀏覽器開(kāi)始樣式計(jì)算,主要是為 DOM 樹(shù)上的節(jié)點(diǎn)找到對(duì)應(yīng)的樣式

  •  構(gòu)建布局樹(shù),樣式計(jì)算完之后就開(kāi)始構(gòu)建布局樹(shù)。主要是為 DOM 樹(shù)上的節(jié)點(diǎn)找到頁(yè)面上對(duì)應(yīng)位置以及一些"display:none"元素的隱藏。

  •  構(gòu)建分層樹(shù),布局樹(shù)完成后瀏覽器還需要建立分層樹(shù),主要是為了滿足滾動(dòng)條,z-index,position 這些復(fù)雜的分層操作

  •  將分層樹(shù)圖塊化,利用光柵找到視圖窗口下的對(duì)應(yīng)的位圖。主要是因?yàn)橐粋€(gè)頁(yè)面可能有幾屏那么長(zhǎng),一下渲染出來(lái)比較浪費(fèi),所以瀏覽器會(huì)找到視圖窗口對(duì)應(yīng)的圖塊,將這部分的圖塊進(jìn)行渲染

  •  最終渲染進(jìn)程將整個(gè)頁(yè)面渲染出來(lái),在渲染的過(guò)程中會(huì)還出現(xiàn)重排和重繪,這也是比較愛(ài)問(wèn)的問(wèn)題"重排重繪為什么會(huì)影響渲染,如何避免?"

  •  以上過(guò)程大概講解了一下從 url 到頁(yè)面渲染的整個(gè)過(guò)程,其實(shí)涉及到了幾個(gè)需要關(guān)注的問(wèn)題,下面來(lái)具體講講

問(wèn)題 2:js css 順序?qū)η岸藘?yōu)化影響

上面我們說(shuō)到了整個(gè)渲染流程,但是沒(méi)有說(shuō)到 css 和 js 對(duì)渲染的影響。渲染樹(shù)的構(gòu)成必須要 DOM 樹(shù)和 CSSOM 樹(shù)的,所以盡快的構(gòu)建 CSSOM 樹(shù)是一個(gè)重要的優(yōu)化手段,如果 css 文件放在尾部,那么整個(gè)過(guò)程就是一個(gè)串行的過(guò)程先解析了 dom,再去解析 css。所以 css 我們一般都是放在頭部,這樣 DOM 樹(shù)和 CSSOM 樹(shù)的構(gòu)建是同步進(jìn)行的。

再來(lái)看 js,因?yàn)?js 的運(yùn)行會(huì)阻止 DOM 樹(shù)的渲染的,所以一旦我們的 js 放在了頭部,而且也沒(méi)有異步加載這些操作的話,js 一旦一直在運(yùn)行,DOM 樹(shù)就一直構(gòu)建不出來(lái),那么頁(yè)面就會(huì)一直出現(xiàn)白屏界面,所以一般我們會(huì)把 js 文件放在尾部。當(dāng)然放到尾部也不是就沒(méi)有問(wèn)題了,只是問(wèn)題相對(duì)較小,放到尾部的 js 文件如果過(guò)大,運(yùn)行時(shí)間長(zhǎng),代碼加載時(shí),就會(huì)有大量耗時(shí)的操作造成頁(yè)面不可點(diǎn)擊,這就是另一個(gè)問(wèn)題,但這肯定比白屏要好,白屏是什么頁(yè)面都沒(méi)有,這種是頁(yè)面有了只是操作不流暢。

js 腳本放在尾部還有一個(gè)原因,有時(shí)候 js 代碼會(huì)有操作 dom 節(jié)點(diǎn)的情況,如果放在頭部執(zhí)行,DOM樹(shù)還沒(méi)有構(gòu)建,拿不到 DOM 節(jié)點(diǎn)但是你又去使用就會(huì)出現(xiàn)報(bào)錯(cuò)情況,錯(cuò)誤沒(méi)處理好的話頁(yè)面會(huì)直接崩掉

問(wèn)題 3:重排重繪為什么會(huì)影響渲染,如何避免?

重排和重繪為什么會(huì)影響渲染,哪個(gè)影響更大,如何避免是經(jīng)常被問(wèn)到的一道題目,我們先來(lái)說(shuō)一下重繪

  •  重繪

        重繪指的是不影響界面布局的操作,比如更改顏色,那么根據(jù)上面的渲染講解我們知道,重繪之后我們只需要在重復(fù)進(jìn)行一下樣式計(jì)算,就可以直接渲染了,對(duì)瀏覽器渲染的影響相對(duì)較小

  •  重排

       重排指的是影響界面布局的操作,比如改變寬高,隱藏節(jié)點(diǎn)等。對(duì)于重排就不是一個(gè)重新計(jì)算樣式那么簡(jiǎn)單了,因?yàn)楦淖兞瞬季?,根?jù)上面的渲染流程來(lái)看涉及到的階段有樣式計(jì)算,布局樹(shù)           重新生成,分層樹(shù)重新生成,所以重排對(duì)瀏覽器的渲染影響是比較高的

  •  避免方法

    •  js 盡量減少對(duì)樣式的操作,能用 css 完成的就用 css

    •  果必須要用 js 操作樣式,能合并盡量合并不要分多次操作

    •  resize 事件 最好加上防抖,能盡量少觸發(fā)就少觸發(fā)

    •  加載對(duì) dom 操作盡量少,能用 createDocumentFragment 的地方盡量用

    •  加圖片的時(shí)候,提前寫(xiě)好寬高

問(wèn)題 4:瀏覽器緩存機(jī)制

瀏覽器緩存是比較常見(jiàn)的問(wèn)題,我會(huì)從瀏覽器緩存的方式,緩存的實(shí)現(xiàn), 緩存在哪里這幾個(gè)點(diǎn)來(lái)說(shuō)明

緩存方式

我們經(jīng)常說(shuō)的瀏覽器緩存有兩種,一種是強(qiáng)制緩存,一種是協(xié)商緩存,因?yàn)橄旅嬗芯唧w實(shí)現(xiàn)講解,所以這里就說(shuō)一下概念

  •  協(xié)商緩存

        協(xié)商緩存意思是文件已經(jīng)被緩存了,但是否從緩存中讀取是需要和服務(wù)器進(jìn)行協(xié)商,具體如何協(xié)商要看請(qǐng)求頭/響應(yīng)頭的字段設(shè)置,下面會(huì)說(shuō)到。需要注意的是協(xié)商緩存還是發(fā)了請(qǐng)求的

  •  強(qiáng)制緩存

         強(qiáng)制緩存就是文件直接從緩存中獲取,不需要發(fā)送請(qǐng)求

緩存實(shí)現(xiàn)

  •  強(qiáng)制緩存

強(qiáng)制緩存在 http1.0 的時(shí)候用的是 Expires,是響應(yīng)頭里面的一個(gè)字段表示的是文件過(guò)期時(shí)間。是一個(gè)絕對(duì)時(shí)間,正因?yàn)槭墙^對(duì)時(shí)間所以在某些情況下,服務(wù)器的時(shí)區(qū)和瀏覽器時(shí)區(qū)不一致的時(shí)候就會(huì)導(dǎo)致緩存失效。為了解決這個(gè)問(wèn)題,HTPP1.1 引入了一個(gè)新的響應(yīng)頭 cache-control 它的可選值如下

cache-control

  •  max-age: 緩存過(guò)期時(shí)間,是一個(gè)相對(duì)時(shí)間

  •  public: 表示客戶端和代理服務(wù)器都會(huì)緩存

  •  private: 表示只在客戶端緩存

  •  no-cache: 協(xié)商緩存標(biāo)識(shí)符,表示文件會(huì)被緩存但是需要和服務(wù)器協(xié)商

  •  no-store: 表示文件不會(huì)被緩存

HTTP1.1 利用的就是 max-age:600 來(lái)強(qiáng)制緩存,因?yàn)槭窍鄬?duì)時(shí)間,所以不會(huì)出現(xiàn) Expires 問(wèn)題

  •  協(xié)商緩存

        協(xié)商緩存是利用 Last-Modified/if-Modified-Since,Etag/if-None-Match 這兩對(duì)請(qǐng)求、響應(yīng)頭。

        Last-Modified/if-Modified-Since

          Etag/If-None-Match

由于 Last-Modified 的時(shí)間粒度是秒,有的文件在 1s 內(nèi)可能被改動(dòng)多次。這種方式在這種特殊情況下還是會(huì)失效,所以HTTP1.1又引入了 Etag 字段。這個(gè)字段是根據(jù)文件內(nèi)容生成一個(gè)標(biāo)記符比如"W/"5f9583bd-10a8"",然后再和 If-None-Match 進(jìn)行對(duì)比就能更準(zhǔn)確的知道文件有沒(méi)有被改動(dòng)過(guò)

  •  瀏覽器第一次發(fā)送請(qǐng)求獲取文件緩存下來(lái),服務(wù)器響應(yīng)頭返回一個(gè) if-Modified-Since,記錄被改動(dòng)的時(shí)間

  •  瀏覽器第二次發(fā)送請(qǐng)求的時(shí)候會(huì)帶上一個(gè) Last-Modified 請(qǐng)求頭,時(shí)間就是 if-Modified-Since 返回的值。然后服務(wù)器拿到這個(gè)字段和自己內(nèi)部設(shè)置的時(shí)間進(jìn)行對(duì)比,時(shí)間相同表示沒(méi)有修改,就直接返回 304 從緩存里面獲取文件

緩存在哪里

知道了緩存方式和實(shí)現(xiàn),再來(lái)說(shuō)一下緩存存在哪個(gè)地方,我們打開(kāi)掘金可以看到如下的信息 。緩存的來(lái)源有兩個(gè)地方 from dist cache,from memeory cache

從前端性能優(yōu)化引申出來(lái)的經(jīng)典面試題有哪些

form memory cache

這個(gè)是緩存在內(nèi)存里面,優(yōu)點(diǎn)是快速,但是具有時(shí)效性,當(dāng)關(guān)閉 tab 時(shí)候緩存就會(huì)失效。

from dist cache

這個(gè)是緩存在磁盤(pán)里面,雖然慢但是還是比請(qǐng)求快,優(yōu)點(diǎn)是緩存可以一直被保留,即使關(guān)閉 tab 頁(yè),也會(huì)一直存在

何時(shí)緩存在memory,合適緩存在dist?

這個(gè)問(wèn)題網(wǎng)上很少找的到標(biāo)準(zhǔn)答案,大家一致的說(shuō)法是js,圖片文件瀏覽器會(huì)自動(dòng)保存在memory中,css文件因?yàn)椴怀P薷谋4嬖赿ist里面,我們可以打開(kāi)掘金網(wǎng)站,很大一部分文件都是按照這個(gè)規(guī)則來(lái)的,但是也有少數(shù)js文件也是緩存在dist里面。所以他的存放機(jī)制到底是什么樣了?我?guī)е@個(gè)疑問(wèn)查了好多文章,雖然最后沒(méi)有確切找到答案,但是一個(gè)知乎的回答可以給我們提供思路,下面引用一個(gè)知乎回答者一段話

  •  第一個(gè)現(xiàn)象(以圖片為例):訪問(wèn)-> 200 -> 退出瀏覽器再進(jìn)來(lái)-> 200(from disk cache) -> 刷新 -> 200(from memory cache)??偨Y(jié): 會(huì)不會(huì)是chrome很聰明的判斷既然已經(jīng)從disk拿來(lái)了, 第二次就內(nèi)存拿吧 快。(笑哭)

  •  第二個(gè)現(xiàn)象(以圖片為例):只要圖片是base64 我看都是from memroy cache??偨Y(jié): 解析渲染圖片這么費(fèi)勁的事情,還是做一次然后放到內(nèi)存吧。用的時(shí)候直接拿

  •  第三個(gè)現(xiàn)象(以js css為例):個(gè)人在做靜態(tài)測(cè)試的發(fā)現(xiàn),大型的js css文件都是直接disk cache。結(jié): chrome會(huì)不會(huì)說(shuō) 我去 你這么大太占地方了。你就去硬盤(pán)里呆著吧。慢就慢點(diǎn)吧。

  •  第四個(gè)現(xiàn)象:隱私模式下,幾乎都是 from memroy cache??偨Y(jié): 隱私模式 是吧。我不能暴露你東西,還是放到內(nèi)存吧。你關(guān),我死。

上面幾點(diǎn)是雖然很幽默,但是卻可以從中找到一部分答案,但是我覺(jué)得另一個(gè)知乎回答我更贊同

瀏覽器運(yùn)行的時(shí)候也是由幾個(gè)進(jìn)程協(xié)作的,所以操作系統(tǒng)為了節(jié)省內(nèi)存,會(huì)把一部分內(nèi)存里的資源交換回磁盤(pán)的交換區(qū),當(dāng)然交換是有策略的,比如最常用的就是LRU。

什么時(shí)候存dist,什么時(shí)候存memoey都是在瀏覽器控制下的,memory不夠了可能就會(huì)考慮去存dist了,所以經(jīng)過(guò)上面所說(shuō)我自己總結(jié)結(jié)果如下

  •  大一點(diǎn)的文件會(huì)緩存在dist里面,因?yàn)閮?nèi)存也是有限的,磁盤(pán)的空間更大

  •  小一點(diǎn)文件js,圖片存的是memory

  •  css文件一般存在dist

  •  特殊情況memory大小是有限制的,瀏覽器也會(huì)根據(jù)自己的內(nèi)置算法,把一部分js文件存到dist里面

問(wèn)題 5:https 和 http 的區(qū)別

說(shuō)到https和http的區(qū)別,可以說(shuō)一下https服務(wù)器和客戶端連接的差異,以及https特定的加密算法協(xié)商,甚至可能還要說(shuō)到對(duì)稱加密,非對(duì)稱加密和證書(shū)等,篇幅很長(zhǎng),請(qǐng)看我之前單獨(dú)寫(xiě)的一篇https詳解,里面講的非常詳細(xì)。

請(qǐng)求優(yōu)化

講請(qǐng)求優(yōu)化的之前先來(lái)總結(jié)下上面說(shuō)到的js, css文件順序優(yōu)化,為了讓渲染更快,我們需要把js放到尾部,css放到頭部,然后還要注意在書(shū)寫(xiě)js的時(shí)候盡量減少重排,重繪。書(shū)寫(xiě)html,css的時(shí)候盡量簡(jiǎn)潔,不要冗余,目的是為了更快的構(gòu)建DOM樹(shù)和CSSOM樹(shù)。好了下面我們?cè)趤?lái)說(shuō)說(shuō)請(qǐng)求優(yōu)化,請(qǐng)求優(yōu)化可以從請(qǐng)求數(shù)量和請(qǐng)求時(shí)間兩方面入手

減少請(qǐng)求數(shù)量

  •  將小圖片打包成base64

  •  利用雪碧圖融合多個(gè)小圖片

  •  利用緩存上面已經(jīng)說(shuō)到過(guò)

減少請(qǐng)求時(shí)間

  •  將js,css,html等文件能壓縮的盡量壓縮,減少文件大小,加快下載速度

  •  利用webpack打包根據(jù)路由進(jìn)行懶加載,不要初始就加載全部,那樣文件會(huì)很大

  •  能升級(jí)到高版本的http就升級(jí)到高版本(這個(gè)回答是套話),為什么高版本能提高速度具體看上面我說(shuō)的那篇https文章

  •  建立內(nèi)部cdn能更快速的獲取文件

webpack優(yōu)化

介紹了渲染優(yōu)化,現(xiàn)在來(lái)看看webpack優(yōu)化,自己平常寫(xiě)demo給團(tuán)隊(duì)做培訓(xùn)的時(shí)候都是自己手寫(xiě)webpack配置,雖然也就幾十行,但每次都能讓我鞏固webpack的基本配置,下面直接說(shuō)一下webpack優(yōu)化手段有哪些

基礎(chǔ)配置優(yōu)化

  •  extensions 這個(gè)配置是屬于resolve里面的,經(jīng)常用來(lái)對(duì)文件后綴進(jìn)行擴(kuò)展,寫(xiě)法如下 

resolve: {      extensions: ['.ts', '.tsx', '.js']  }

這個(gè)配置表示webpack會(huì)根據(jù)extensions去尋找文件后綴名,所以如果我們的項(xiàng)目主要用ts寫(xiě)的話,那我們就可以.tsx和.ts寫(xiě)前面,目的是為了讓webpack能夠快速解析

  •  alias 這個(gè)配置也是屬于resolve里面的,是用來(lái)映射路勁,能減少打包時(shí)間的主要原因是能夠讓webpack快速的解析文件路徑,找到對(duì)應(yīng)的文件,配置如下 

resolve: {    alias: {      Components: path.resolve(__dirname, './src/components')    }  }
  •  noParse

noParse表示不需要解析的文件,有的文件可能是來(lái)自第三方的文件,被 providePlugin引入作為windows上的變量來(lái)使用,這樣的文件相對(duì)比較大,并且已經(jīng)是被打包過(guò)的,所以把這種文件排除在外是很有必要的,配置如下

module: {    noParse: [/proj4\.js/]  }
  •  exclude

某些loader會(huì)有這樣一個(gè)屬性,目的是指定loader作用的范圍,exclude表示排除某些文件不需要babel-loader處理,loader的作用范圍小了,打包速度自然就快了,用babel-loader舉一個(gè)簡(jiǎn)單例子

{      test: /\.js$/,      loader: "babel-loader",      exclude: path.resolve(__dirname, 'node_modules')  }
  •  devtool

這個(gè)配置是一個(gè)調(diào)試項(xiàng),不同的配置展示效果不一樣,打包大小和打包速度也不一樣,比如開(kāi)發(fā)環(huán)境下cheap-source-map肯定比source-map快,至于為什么,強(qiáng)烈推薦自己之前寫(xiě)的這一篇講解devtool的文章:webpack devtools篇講的非常詳細(xì)。

{      devtool: 'cheap-source-map'  }

.eslintignore

這個(gè)雖不是webpack配置但是對(duì)打包速度優(yōu)化還是很有用的,在我的實(shí)踐中eslint檢查對(duì)打包的速度影響很大,但是很多情況我們不能沒(méi)有這個(gè)eslint檢查,eslint檢查如果僅僅在vs里面開(kāi)啟的話,可能不怎么保險(xiǎn)。

因?yàn)橛锌赡苣鉽s中的eslint插件突然關(guān)閉了或者某些原因vs不能檢查了,只能靠webpack構(gòu)建去幫你攔住錯(cuò)誤代碼的提交,即使這樣還不能確保萬(wàn)無(wú)一失,因?yàn)槟憧赡苣骋淮翁峤淮a很急沒(méi)有啟動(dòng)服務(wù),直接盲改提交上去了。這個(gè)時(shí)候只能通過(guò)最后一道屏障給你保護(hù),就是在CI的時(shí)候。比如我們也會(huì)是在jenkins構(gòu)建的時(shí)候幫你進(jìn)行eslint檢查,三道屏障確保了我們最終出的鏡像是不會(huì)有問(wèn)題的。

所以eslint是很重要的,不能刪掉,在不能刪掉的情況下怎么讓檢查的時(shí)間更少了,我們就可以通過(guò)忽略文件,讓不必要的文件禁止eslint,只對(duì)需要的文件eslint可以很大程度提高打包速度

loader,plugins優(yōu)化

上述說(shuō)了幾個(gè)基礎(chǔ)配置優(yōu)化,應(yīng)該還有其他的基礎(chǔ)配置,今后遇到了再繼續(xù)添加,現(xiàn)在在來(lái)講講利用某些loader,plugins來(lái)提高打包速度的例子

  •  cache-loader

這個(gè)loader就是在第一次打包的時(shí)候會(huì)緩存打包的結(jié)果,在第二次打包的時(shí)候就會(huì)直接讀取緩存的內(nèi)容,從而提高打包效率。但是也需要合理利用,我們要記住一點(diǎn)你加的每一個(gè)loader,plugins都會(huì)帶來(lái)額外的打包時(shí)間。這個(gè)額外時(shí)間比他帶來(lái)的減少時(shí)間多,那么一味的增加這個(gè)loader就沒(méi)意義,所以cache-loader最好用在耗時(shí)比較大的loader上,配置如下

{    rules: [      {        test: /\.vue$/,        use: [          'cache-loader',          'vue-loader'        ],        include: path.resolve(__dirname, './src')      }    ]  }
  •  webpack-parallel-uglify-plugin, uglifyjs-webpack-plugin, terser-webpack-plugin

在上面的渲染優(yōu)化中我們已經(jīng)知道,文件越小渲染的速度是越快的。所以我們?cè)谂渲脀ebpack時(shí)候經(jīng)常會(huì)用到壓縮,但是壓縮也是需要消耗時(shí)間的,所以我們我們經(jīng)常會(huì)用到上面三個(gè)插件之一來(lái)開(kāi)啟并行壓縮,減少壓縮時(shí)間,我們用webpack4推薦使用的terse-webpack-plugin做例子來(lái)說(shuō)明

optimization: {    minimizer: [        new TerserPlugin({          parallel: true,          cache: true        })      ],  }
  •  happypack, parallel-webpack, thread-loader

這幾個(gè)loader/plugin和上面一樣也是開(kāi)啟并行的,只不過(guò)是開(kāi)啟并行構(gòu)建。由于happypack的作者說(shuō)自己的興趣已經(jīng)不再js上了,所以已經(jīng)沒(méi)有維護(hù)了,并推薦如果使用的是webpack4的話,就去使用thread-loader。基本配置如下

{     test: /\.js$/,     use: [       {         loader: "thread-loader",         options: threadLoaderOptions       },       "babel-loader",     ],     exclude: /node_modules/,   }
  •  DllPlugin,webpack.DllReferencePlugin

上面說(shuō)的幾個(gè)并行插件理論上是可以增加構(gòu)建速度,網(wǎng)上很多文章都是這么說(shuō)的,但是我在實(shí)際的過(guò)程中使用,發(fā)現(xiàn)有時(shí)候不僅沒(méi)提升反而還降低了打包速度,網(wǎng)速查閱給的理由是可能你的電腦核數(shù)本來(lái)就低,或者當(dāng)時(shí)你CPU運(yùn)行已經(jīng)很高了,再去開(kāi)啟多進(jìn)程導(dǎo)致構(gòu)建速度降低。

上面說(shuō)的幾個(gè)并行插件可能在某些情況下達(dá)不到你想要的效果,然而在我們團(tuán)隊(duì)優(yōu)化webpack性能經(jīng)驗(yàn)來(lái)看,這次所說(shuō)的兩個(gè)插件是很明顯并且每次都能提高打包速度的。原理就是先把第三方依賴先打包一次生成一個(gè)js文件,然后真正打包項(xiàng)目代碼時(shí)候,會(huì)根據(jù)映射文件直接從打包出來(lái)的js文件獲取所需要的對(duì)象,而不用再去打包第三方文件。只不過(guò)這種情況打包配置稍微麻煩點(diǎn),需要寫(xiě)一個(gè)webpack.dll.js。大致如下

webpack.dll.js

const path = require('path');  const webpack = require('webpack');  module.exports = {      entry: {          library: ["vue", "moment"]      },      output: {          filename: '[name].dll.js',          path: path.resolve(__dirname, 'json-dll'),          library: '[name]'      },      plugins: [          new webpack.DllPlugin({              path: './json-dll/library.json',              name: '[name].json'          })      ]  }

webpack.dev.js

new AddAssetHtmlWebpack({     filepath: path.resolve(__dirname, './json-dll/library.dll.js')  }),  ew webpack.DllReferencePlugin({      manifest: require("./json-dll/library.json")    })

其他優(yōu)化配置

這些插件就簡(jiǎn)單的介紹下,在我的個(gè)人項(xiàng)目中已經(jīng)使用過(guò),自我感覺(jué)還可以,具體使用可以查閱npm或者github

  •  webpack-bundle-analyzer

這個(gè)插件可以用可視化幫我們分析打包體積,從而來(lái)采用合適的優(yōu)化方式去改進(jìn)我們的webpack配置

  •  speed-measure-webpack-plugin

這個(gè)插件可以告訴我們打包時(shí)候每一個(gè)loader或者plugin花費(fèi)了多少時(shí)間,從而對(duì)耗時(shí)比較長(zhǎng)的plugin和loader做優(yōu)化

  •  friendly-errors-webpack-plugin

這個(gè)插件可以幫我們優(yōu)化打包日志,我們打包時(shí)候經(jīng)??吹胶荛L(zhǎng)一個(gè)日志信息,有的時(shí)候是不需要的,也不會(huì)去看所以可以用這個(gè)插件來(lái)簡(jiǎn)化

代碼優(yōu)化

這是最后一部分代碼優(yōu)化了,這里的代碼性能優(yōu)化我只說(shuō)我在工作中感受到的,至于其他的比較小的優(yōu)化點(diǎn)比如createDocumentFragment使用可以查查其他文章

能不操作dom不要操作dom,哪怕有時(shí)候需要改設(shè)計(jì)

很多情況下我們都能用css還原設(shè)計(jì)稿,但是有些時(shí)候單單從css沒(méi)法還原,尤其組件還不是你寫(xiě)的時(shí)候,比如我們團(tuán)隊(duì)用的就是antd,有時(shí)候的產(chǎn)品設(shè)計(jì)單從css上沒(méi)法實(shí)現(xiàn),只能動(dòng)用js,刪除,增加節(jié)點(diǎn)在配合樣式才能完成。

由于我們又是一個(gè)做大數(shù)據(jù)的公司,這個(gè)時(shí)候就會(huì)出現(xiàn)性能問(wèn)題,最開(kāi)始寫(xiě)代碼時(shí)候,產(chǎn)品說(shuō)什么就是什么,說(shuō)什么我都會(huì)想辦法搞出來(lái),不管用什么方法。后來(lái)到客戶現(xiàn)場(chǎng)大數(shù)據(jù)請(qǐng)況下,性能缺點(diǎn)立馬暴露的出來(lái)。

所以代碼優(yōu)化的原則之一我認(rèn)為是能不寫(xiě)的代碼就不寫(xiě),當(dāng)然這是要從性能角度出發(fā),通過(guò)性能分析給產(chǎn)品說(shuō)出理由,并且最好還能提供更好的解決方案,這個(gè)才是我們需要考慮的。

如果用的是react 一定用寫(xiě)shouldComponentUpdate這個(gè)生命周期函數(shù),不然打印的時(shí)候你會(huì)發(fā)現(xiàn),你自己都迷糊為什么執(zhí)行了這么多遍

將復(fù)雜的比對(duì),變成簡(jiǎn)單比對(duì)

這句話是什么意思了?我們就拿shouldComponentUpdate舉例子,用這個(gè)函數(shù)沒(méi)問(wèn)題,但是可以做的更好,我們?cè)诠ぷ髦薪?jīng)常這么寫(xiě)

shouldComponentUpdate(nextPrpops) {    return JSON.stringify(nextPrpops.data) !== JSON.stringify(this.props.data)  }

如果這是一個(gè)分頁(yè)表格,data是每一頁(yè)數(shù)據(jù),數(shù)據(jù)改變了重新渲染,在小數(shù)據(jù)場(chǎng)景下這本身是沒(méi)有問(wèn)題。但是如果在大數(shù)據(jù)的場(chǎng)景下可能會(huì)有問(wèn)題,可能有人有疑問(wèn),既然做了分頁(yè)怎么還會(huì)有大數(shù)據(jù)了,因?yàn)槲覀兊漠a(chǎn)品是做大數(shù)據(jù)分析日志的,一頁(yè)十條日志,有的日志可能非常的長(zhǎng),也就是說(shuō)就算是10條數(shù)據(jù)比對(duì)起來(lái)也是很耗時(shí),所以當(dāng)時(shí)想法能不能找到其他的替代變量來(lái)表示數(shù)據(jù)變了?比如下面這樣

shouldComponentUpdate(nextPrpops) {    return nextPrpops.data[0].id !== this.props.data[0].id  }

第一條的id不一樣就表示數(shù)據(jù)變化了行不行,顯然在某種情況下是存在的,也有人會(huì)說(shuō)可能會(huì)出現(xiàn)id一樣,那如果換成下面這種了?

shouldComponentUpdate(nextPrpops) {    return nextPrpops.current !== this.props.current  }

將data的比對(duì)轉(zhuǎn)換成了current的比對(duì),因?yàn)轫?yè)數(shù)變了,數(shù)據(jù)基本都是變了,對(duì)于我們自己日志的展示來(lái)說(shuō)基本不存在兩頁(yè)數(shù)據(jù)是一模一樣的,如果有那可能是后臺(tái)問(wèn)題。然后在好好思考這個(gè)問(wèn)題,即使存在了兩頁(yè)數(shù)據(jù)一摸一樣,頂多就是這個(gè)表格不重新渲染,但是兩頁(yè)數(shù)據(jù)一摸一樣不重新渲染是不是也沒(méi)有問(wèn)題,因?yàn)閿?shù)據(jù)是一樣的。或者如果你還是不放心,那下面這種會(huì)不會(huì)好點(diǎn)

this.setState({    data,    requestId: guid()  })  shouldComponentUpdate(nextPrpops) {    return nextPrpops.requestId !== this.props.requestId  }

給一個(gè)requestId跟宗data,后面就只比對(duì)requestId。上面的寫(xiě)法可能都有問(wèn)題,但是主要是想說(shuō)的是我們?cè)趯?xiě)代碼時(shí)候可以想想是不是可以"將復(fù)雜的比對(duì),變成簡(jiǎn)單比對(duì)"

學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法,一定會(huì)在你的工作中派上用場(chǎng)

我們經(jīng)常會(huì)聽(tīng)到學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法沒(méi)有什么大的用處,因?yàn)楣ぷ骰居貌簧稀_@句話我之前覺(jué)得沒(méi)錯(cuò),現(xiàn)在看來(lái)錯(cuò)的很嚴(yán)重。我們所學(xué)的每一樣技能,都會(huì)在將來(lái)的人生中派上用場(chǎng)。之前寫(xiě)完代碼就丟了不去優(yōu)化所以我覺(jué)得算法沒(méi)意義,又難又容易忘記。但現(xiàn)在要求自己做完需求,開(kāi)啟mock,打開(kāi)perfermance進(jìn)行大數(shù)據(jù)量的測(cè)試,看著那些標(biāo)紅的火焰圖和肉眼可見(jiàn)的卡頓,就明白了算法和數(shù)據(jù)結(jié)構(gòu)的重要性,因?yàn)榇藭r(shí)你只能從它身上獲取優(yōu)化,平時(shí)你很排斥它,到優(yōu)化的時(shí)候你是那么想擁有它。我拿自己之前寫(xiě)的代碼舉例,由于公司代碼是保密的我就把變量換一下,偽代碼如下

data.filter(({id}) => {    return selectedIds.includes(id);  })

就是這樣幾行代碼,邏輯就是篩選出data里面已經(jīng)被勾選的數(shù)據(jù)?;旧虾芏嗳硕伎赡苓@么寫(xiě),因?yàn)槲铱次覀儓F(tuán)隊(duì)里面都是這么寫(xiě)的。產(chǎn)品當(dāng)時(shí)已經(jīng)限制data最多200數(shù)據(jù),所以寫(xiě)完完全沒(méi)壓力,性能沒(méi)影響。但是秉著對(duì)性能優(yōu)化的原則(主要是被現(xiàn)場(chǎng)環(huán)境搞怕了~~~),我開(kāi)啟了mock服務(wù),將數(shù)據(jù)調(diào)到了2萬(wàn)條再去測(cè)試,代碼弊端就暴露出來(lái)了,界面進(jìn)入卡頓,重新選擇的時(shí)候也會(huì)卡頓。然后就開(kāi)始了優(yōu)化,當(dāng)時(shí)具體的思路如下

按照現(xiàn)在的代碼來(lái)看,這是一個(gè)兩層循環(huán)的暴力搜索時(shí)間復(fù)雜度為O(n^2)。所以想著能不能降一下復(fù)雜度至少是O(nlogn),看了一下代碼只能從selectedIds.includes(id)這句入手,于是想著可不可以用二分,但是立馬被否定因?yàn)槎质切枰行虻?,我這數(shù)組都是字符串怎么二分。

安靜了一下之后,回想起看過(guò)的算法課程和書(shū)籍以及做的算法題,改變暴力搜索的方法基本都是

1:上指針

2:數(shù)組升維

3:利用hash表

前兩者被我否定了因?yàn)槲矣X(jué)得還沒(méi)那么復(fù)雜,于是利用hash表思想解決這個(gè)問(wèn)題,因?yàn)閖s里面有一個(gè)天然的hash表結(jié)構(gòu)就是對(duì)象。我們知道hash表的查詢是O(1)的,所以我將代碼改寫(xiě)如下

const ids = {};  selectedIds.forEach(id => ids[id] = 1);  data.filter(({id}) => {    return !!ids[id];  })

將從selectedIds查詢變成從ids查詢,這樣時(shí)間復(fù)雜度就從O(n^2)變成了O(n)了,這段代碼增加了

const ids = {};  selectedIds.forEach(id => ids[id] = 1);

其實(shí)增加了一個(gè)selectedIds遍歷也是一個(gè)O(n)的復(fù)雜度,總來(lái)說(shuō)復(fù)雜度是O(2n),但是從時(shí)間復(fù)雜度長(zhǎng)期期望來(lái)看還是一個(gè)O(n)的時(shí)間復(fù)雜度,只不過(guò)額外增加了一個(gè)對(duì)象,所以這也是一個(gè)典型的空間換時(shí)間的例子,但是也不要擔(dān)心,ids用完之后垃圾回收機(jī)制會(huì)把他回收的。

“從前端性能優(yōu)化引申出來(lái)的經(jīng)典面試題有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

網(wǎng)站題目:從前端性能優(yōu)化引申出來(lái)的經(jīng)典面試題有哪些
URL網(wǎng)址:http://bm7419.com/article38/pcdisp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)關(guān)鍵詞優(yōu)化、網(wǎng)站改版、品牌網(wǎng)站設(shè)計(jì)網(wǎng)站策劃、移動(dòng)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

營(yíng)銷型網(wǎng)站建設(shè)