對于前端性能優(yōu)化的理解與實踐

先從一道題目說起

從輸入 URL 到頁面加載完成剧罩,發(fā)生了什么座泳?

  • 站在性能優(yōu)化的角度幕与;我們可以分為5個過程啦鸣;
  1. DNS 解析
  2. TCP 連接
  3. HTTP 請求拋出
  4. 服務(wù)端處理請求,HTTP 響應(yīng)返回
  5. 瀏覽器拿到響應(yīng)數(shù)據(jù)诫给,解析響應(yīng)內(nèi)容啦扬,把解析的結(jié)果展示給用戶
  • 我們從這五個過程個個擊破;
    dns解析花時間扑毡,tcp連接慢;這些需要我們的服務(wù)端解決勋又;
    那么我們的前端工程師在HTTP請求或者瀏覽器端能做一些什么優(yōu)化呢泉褐?
    http方面前端可以減少請求次數(shù),壓縮體積膜赃;
    瀏覽器前端可以做的事情比較多了,例如資源加載優(yōu)化端铛、服務(wù)端渲染疲眷、瀏覽器緩存機制的利用、DOM 樹的構(gòu)建狂丝、網(wǎng)頁排版和渲染過程、回流與重繪的考量倍试、DOM 操作的合理規(guī)避


    1669f5358f63c0f8 (1).png

網(wǎng)絡(luò)層面(http請求優(yōu)化and減少網(wǎng)絡(luò)請求)

webpack打包體積優(yōu)化
  • webpack-bundle-analyzer 是一款包可視化工具蛋哭,可以找出體積大的模塊;
  • 刪除冗余代碼 webpack3可以使用UglifyJsPlugin 躁愿;webpack4已經(jīng)自帶了,只需要配置下彤钟;
  • 按需加載 vue項目可以用require.ensure來實現(xiàn)
  • gzip 本來是服務(wù)端的工作,webpack也有g(shù)zip可以幫助服務(wù)端減輕壓力
圖片
  • JPEG/JPG:有損壓縮吠勘、體積小性芬、加載快、不支持透明辫樱;龐大的圖片用jpg
  • PNG-8 與 PNG-24:無損壓縮俊庇、質(zhì)量高、體積大辉饱、支持透明 像logo類等比較突出的最好用png
  • SVG (字體圖標):文本文件、體積小缔逛、不失真、兼容性好
  • Base64 :文本文件褐奴、依賴編碼于毙、小圖標解決方案,Base64 是作為雪碧圖的補充而存在的;Base64 編碼后,圖片大小會膨脹為原文件的 4/3;在傳輸非常小的圖片的時候脖旱,Base64 帶來的文件體積膨脹、以及瀏覽器解析 Base64 的時間開銷萌庆,與它節(jié)省掉的 HTTP 請求開銷相比甘耿,可以忽略不計
  • CSS Sprites(精靈圖/雪碧圖):小圖標解決方案
  • WebP :與 PNG 相比,WebP 無損圖像的尺寸縮小了 26%佳恬。限制WebP發(fā)展的是瀏覽器兼容問題于游;
瀏覽器緩存
  • Memory Cache 內(nèi)存緩存是快的垫言,也是“短命”的。
  • Service Worker Cache 幫我們實現(xiàn)離線緩存蚌成、消息推送和網(wǎng)絡(luò)代理等功能,但必需以https 協(xié)議為前提
  • Push Cache HTTP2存在担忧,Push Cache 是緩存的最后一道防線坯癣,會話階段的緩存;
  • HTTP Cache (主要示罗、最具有代表性的)

HTTP緩存分為強緩存和協(xié)商緩存
強緩存:Expires 和 Cache-Control (http1.1新增)兩個字段來控制
expires 能做的事情,Cache-Control 都能做轧房;expires 完成不了的事情绍绘,Cache-Control 也能做。因此脯倒,Cache-Control 可以視作是 expires 的完全替代方案。Cache-Control 相對于 expires 更加準確剪撬,它的優(yōu)先級也更高悠反。當 Cache-Control 與 expires 同時出現(xiàn)時,我們以 Cache-Control 為準斋否。

public 與 private 是針對資源是否能夠被代理服務(wù)緩存而存在的一組對立概念。

no-cache 繞開了瀏覽器:我們?yōu)橘Y源設(shè)置了 no-cache 后疫诽,每一次發(fā)起請求都不會再去詢問瀏覽器的緩存情況,而是直接向服務(wù)端去確認該資源是否過期奇徒;no-store 比較絕情,顧名思義就是不使用任何緩存策略罢低。在 no-cache 的基礎(chǔ)上,它連服務(wù)端的緩存確認也繞開了网持,只允許你直接向服務(wù)端發(fā)送請求长踊、并下載完整的響應(yīng)。

協(xié)商緩存:協(xié)商緩存機制下日杈,瀏覽器需要向服務(wù)器去詢問緩存的相關(guān)信息,進而判斷是重新發(fā)起請求、下載完整的響應(yīng)酿炸,還是從本地獲取緩存的資源。資源會被重定向到瀏覽器緩存填硕,這種情況下網(wǎng)絡(luò)請求對應(yīng)的狀態(tài)碼是 304

165f701820fafcf8 (1).png

當我們的資源內(nèi)容不可復(fù)用時扁眯,直接為 Cache-Control 設(shè)置 no-store,拒絕一切形式的緩存姻檀;否則考慮是否每次都需要向服務(wù)器進行緩存有效確認,如果需要胶台,那么設(shè) Cache-Control 的值為 no-cache;否則考慮該資源是否可以被代理服務(wù)器緩存诈唬,根據(jù)其結(jié)果決定是設(shè)置為 private 還是 public缩麸;然后考慮該資源的過期時間,設(shè)置對應(yīng)的 max-age 和 s-maxage 值;最后济竹,配置協(xié)商緩存需要用到的 Etag霎槐、Last-Modified 等參數(shù)。

本地存儲

  • cookie 只能存儲4KB 丘跌,緊跟域名的
  • Web Storage Local Storage和Session Storage 這兩個對前端來說很熟悉了;
  • IndexDB 運行在瀏覽器上的非關(guān)系型數(shù)據(jù)庫耸棒;IndexDB 是沒有存儲上限的(一般來說不會小于 250M)

cdn

  • 內(nèi)容分發(fā)網(wǎng)絡(luò)
  • 緩存和回源报辱。

緩存”就是說我們把資源 copy 一份到 CDN 服務(wù)器上這個過程,“回源”就是說 CDN 發(fā)現(xiàn)自己沒有這個資源(一般是緩存的數(shù)據(jù)過期了)碍现,轉(zhuǎn)頭向根服務(wù)器(指業(yè)務(wù)服務(wù)器)或者它的上層服務(wù)器去要這個資源的過程。

  • CDN 往往被用來存放靜態(tài)資源

所謂“靜態(tài)資源”爽篷,就是像 JS、CSS逐工、圖片等不需要業(yè)務(wù)服務(wù)器進行計算即得的資源漂辐。而“動態(tài)資源”,顧名思義是需要后端實時動態(tài)生成的資源髓涯,較為常見的就是 JSP、ASP 或者依賴服務(wù)端渲染得到的 HTML 頁面瘤泪。

  • 性能優(yōu)化方面的應(yīng)用

同一個域名下的請求會不分青紅皂白地攜帶 Cookie育八,而靜態(tài)資源往往并不需要 Cookie 攜帶什么認證信息。把靜態(tài)資源和主頁面置于不同的域名下髓棋,完美地避免了不必要的 Cookie 的出現(xiàn)惶洲!

服務(wù)端渲染(SSR)

  • 客戶端渲染:需要把js文件跑完膳犹,生成對應(yīng)的dom樹;
  • 服務(wù)端渲染:直接拿到服務(wù)端放回的html就可以呈現(xiàn)在用戶面前
  • 質(zhì)上是本該瀏覽器做的事情铐料,分擔(dān)給服務(wù)器去做。這樣當資源抵達瀏覽器時钠惩,它呈現(xiàn)的速度就快了族阅。

CSSOM,JS的優(yōu)化

瀏覽器背后的運行機制
QQ截圖20181123134723.png
165f7018d20fafcf8 (1).png
CSS 選擇符是從右到左進行匹配的

避免使用通配符愧沟,只對需要用到的元素進行選擇鲤遥。
關(guān)注可以通過繼承實現(xiàn)的屬性,避免重復(fù)匹配重復(fù)定義渴频。
少用標簽選擇器。如果可以,用類選擇器替代
不要畫蛇添足咕村,id 和 class 選擇器不應(yīng)該被多余的標簽選擇器拖后腿。
減少嵌套懈涛。后代選擇器的開銷是最高的,因此我們應(yīng)該盡量將選擇器的深度降到最低

JS的加載方式

正常模式 <script src="index.js"></script>
async 模式 <script async src="index.js"></script>
defer 模式 <script defer src="index.js"></script>
defer 模式下宇植,JS 的加載是異步的,執(zhí)行是被推遲的指郁。等整個文檔解析完成拷呆、DOMContentLoaded 事件即將被觸發(fā)時疫粥,被標記了 defer 的 JS 文件才會開始依次執(zhí)行腰懂。
從應(yīng)用的角度來說,一般當我們的腳本與 DOM 元素和其它腳本之間的依賴關(guān)系不強時绣溜,我們會選用 async;當腳本依賴于 DOM 元素和其它腳本的執(zhí)行結(jié)果時底哗,我們會選用 defer。

DOM的優(yōu)化

  • 回流和重繪

回流:當我們對 DOM 的修改引發(fā)了 DOM 幾何尺寸的變化(比如修改元素的寬艘虎、高或隱藏元素等)時咒吐,瀏覽器需要重新計算元素的幾何屬性(其他元素的幾何屬性和位置也會因此受到影響),然后再將計算的結(jié)果繪制出來恬叹。這個過程就是回流(也叫重排)。
重繪:當我們對 DOM 的修改導(dǎo)致了樣式的變化绽昼、卻并未影響其幾何屬性(比如修改了顏色或背景色)時,瀏覽器不需重新計算元素的幾何屬性目溉、直接為該元素繪制新的樣式(跳過了上圖所示的回流環(huán)節(jié))。這個過程叫做重繪缭付。
由此我們可以看出循未,重繪不一定導(dǎo)致回流,回流一定會導(dǎo)致重繪的妖。我們對dom的優(yōu)化主要在于減少DOM操作;

  • 回流的“導(dǎo)火索”

改變 DOM 元素的幾何屬性
改變 DOM 樹的結(jié)構(gòu)
獲取一些特定屬性的值:offsetTop娇未、offsetLeft、 offsetWidth忘蟹、offsetHeight、scrollTop媚值、scrollLeft、scrollWidth褥芒、scrollHeight、clientTop锰扶、clientLeft、clientWidth罕偎、clientHeight...

  • 規(guī)避回流與重繪

js先用變量保存好要計算的值,最終再設(shè)置dom
避免逐條改變樣式颜及,使用類名去合并樣式
將 DOM “離線”,先設(shè)置display:none;中間操作蹂楣,后面再設(shè)置display:block;

  • 瀏覽器Flush 隊列
  • DOM Fragment 需要了解一下

本質(zhì)上是作為脫離了真實 DOM 樹的容器出現(xiàn),用于緩存批量化的 DOM 操作

Event Loop 與異步更新策略

  • macro(洪任務(wù)): setTimeout痊土、setInterval、 setImmediate犯祠、script(整體代碼)、 I/O 操作雷则、UI 渲染等肪笋。
  • micro-task(微任務(wù)): process.nextTick度迂、Promise、MutationObserver

當我們需要在異步任務(wù)中實現(xiàn) DOM 修改時惭墓,把它包裝成 micro 任務(wù)是相對明智的選擇坛梁。

  • Vue狀態(tài)更新手法:nextTick
export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  // 檢查上一個異步任務(wù)隊列(即名為callbacks的任務(wù)數(shù)組)是否派發(fā)和執(zhí)行完畢了划咐。pending此處相當于一個鎖
  if (!pending) {
    // 若上一個異步任務(wù)隊列已經(jīng)執(zhí)行完畢,則將pending設(shè)定為true(把鎖鎖上)
    pending = true
    // 是否要求一定要派發(fā)為macro任務(wù)
    if (useMacroTask) {
      macroTimerFunc()
    } else {
      // 如果不說明一定要macro 你們就全都是micro
      microTimerFunc()
    }
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

實際上也是運用了promise

lazy-load(懶加載)

在懶加載的實現(xiàn)中褐缠,有兩個關(guān)鍵的數(shù)值:一個是當前可視區(qū)域的高度,另一個是元素距離可視區(qū)域頂部的高度队魏。

事件的節(jié)流(throttle)與防抖(debounce)

像scroll,resize官帘,keyup等事件頻繁觸發(fā)會引發(fā)頁面的抖動甚至卡頓
節(jié)流”與“防抖”是以閉包的形式來實現(xiàn)的;
它們通過對事件對應(yīng)的回調(diào)函數(shù)進行包裹刽虹、以自由變量的形式緩存時間信息呢诬,最后用 setTimeout 來控制事件的觸發(fā)頻率。

  • 節(jié)流和防抖結(jié)合體
// fn是我們需要包裝的事件回調(diào), delay是時間間隔的閾值
function throttle(fn, delay) {
  // last為上一次觸發(fā)回調(diào)的時間, timer是定時器
  let last = 0, timer = null
  // 將throttle處理結(jié)果當作函數(shù)返回
  
  return function () { 
    // 保留調(diào)用時的this上下文
    let context = this
    // 保留調(diào)用時傳入的參數(shù)
    let args = arguments
    // 記錄本次觸發(fā)回調(diào)的時間
    let now = +new Date()
    
    // 判斷上次觸發(fā)的時間和本次觸發(fā)的時間差是否小于時間間隔的閾值
    if (now - last < delay) {
    // 如果時間間隔小于我們設(shè)定的時間間隔閾值膛虫,則為本次觸發(fā)操作設(shè)立一個新的定時器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 如果時間間隔超出了我們設(shè)定的時間間隔閾值,那就不等了稍刀,無論如何要反饋給用戶一次響應(yīng)
        last = now
        fn.apply(context, args)
    }
  }
}

// 用新的throttle包裝scroll的回調(diào)
document.addEventListener('scroll', throttle(() => console.log('觸發(fā)了滾動事件'), 1000))...

谷歌瀏覽器自帶的Performance以及瀏覽器插件LightHouse可以監(jiān)測網(wǎng)站性能敞曹;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市局齿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抓歼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谣妻,死亡現(xiàn)場離奇詭異卒稳,居然都是意外死亡,警方通過查閱死者的電腦和手機减江,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辈灼,“玉大人,你說我怎么就攤上這事茵休。” “怎么了榕莺?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钉鸯。 經(jīng)常有香客問我,道長贸营,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任钞脂,我火速辦了婚禮捕儒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刘莹。我一直安慰自己,他們只是感情好点弯,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狼钮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪燃领。 梳的紋絲不亂的頭發(fā)上锦援,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天灵寺,我揣著相機與錄音曼库,去河邊找鬼略板。 笑死,一個胖子當著我的面吹牛叮称,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赂韵,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挠蛉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谴古?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掰担,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后带饱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡月趟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年恢口,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耕肩。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖婚被,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情址芯,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布谷炸,位于F島的核電站,受9級特大地震影響旬陡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜描孟,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望场航。 院中可真熱鬧,春花似錦旗闽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至此迅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耸序,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工坎怪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搅窿。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像闹司,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子游桩,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容