鳥瞰前端 , 再論性能優化

歡迎你們前往騰訊雲技術社區,獲取更多騰訊海量技術實踐乾貨哦~css

導語 : 從事前端有6年+的時間了,從最開始的美工到重構再到偏向js邏輯開發的前端開發,一直在前端這個行業裏面摸索和學習,我如今將本身這些年的一個心得體會來個系統性的梳理寫成一篇關於性能優化的主題文章,但願對你們有點幫助,也歡迎你們提出各類意見和建議。html

做者:劉勇剛 前端

前端工程師是一個最近這5-6年纔開始慢慢被互聯網公司重視起來的一個職業,能夠說是一個新興行業,我用一張簡單的思惟導圖帶你們回顧一下前端技術發展的歷程以及將來一個展望:html5

圖片描述

1.0時代沒什麼說的,html、css打天下的時代,那個時候你會用js開發個計算器就牛逼到不行。2.0時代是最好的時代,新技術、新思想蓬勃發展,堪稱前端的工業革命,前端人員的地位獲得了充分承認,門檻也有必定的提高。前端性能優化的涉及點從服務器到協議再到宿主環境自己都要有比較深入的認識,業界目前主要仍是以雅虎總結出來35條前端性能優化的黃金軍規(http://www.cnblogs.com/siqi/p...) 爲參考。今天我想將這些年對前端的性能優化的經驗思考總體來個串燒,帶你們鳥瞰一下前端性能優化目前的一些通行作法以及這麼作的出發點。文章初衷主要是對一些性能優化基礎知識回顧和體系梳理,不對具體技術點作深刻分析,點到爲止,我的理解不對的地方歡迎各位大神拍磚,拋磚引玉。node

引入話題前我仍是先從一個老生常談的話題開始:webpack

「從用戶輸入URl到頁面展現給用戶瀏覽器客戶端的過程當中發生了什麼?」css3

這裏用個圖表簡單描述一下幾個步驟:web

圖片描述

web優化的目標就是如何讓用戶更快、更簡單易用、更流暢的使用咱們的服務,對於前端開發而言就是如何讓咱們的資源體量更小、數量更精簡、內容更早呈現、交互更加人性化。ajax

web性能優化有個你們比較公認的二八原則,就是資源從服務器處理完下發到客戶端的瀏覽器上(上圖第6步)所佔的時間比例大概是整個過程的20%,也就是說服務器端能夠優化的空間的效率提高並不會很明顯,前端性能優化成爲web性能優化重點考慮的領域,我下面將會從如下幾個維度去作了本身的一個思考(跟35條軍規有必定重疊)和總結:算法

圖片描述

1、瀏覽器宿主環境

一、突破單線程解析渲染阻塞限制

瀏覽器是一個單線程解析模式去解析渲染從服務器端拿到的html文本,css加載的過程當中會對後續的腳本資源加載形成阻塞,腳本的加載也會阻塞後續DOM結構的解析形成頁面的留白時間增加,雅虎的35條軍規中有一條就是樣式文件放在頭部,腳本文件放在DOM節點最末尾,減小阻塞。這裏還有幾個針對腳本文件的優化:

針對不須要DOM操做(主要考慮是須要操做DOM的腳本每每須要獲取一些樣式信息)的Js腳本能夠採用動態建立script的方式載入,動態載入的腳本不阻塞後續資源的加載。
腳本文件加載能夠加上defer或者async屬性標識防止阻塞(關於二者區別能夠參考)

二、利用事件冒泡特性

瀏覽器的事件模型的冒泡的特性(瀏覽器事件模型不清楚的自行搜索瞭解)我以爲是最牛逼的設計之一,解決了瀏覽器由於解析DOM模型不一樣步致使開發者往DOM對象註冊事件回調找不到對象的問題。

瀏覽器事件註冊有3個級別定義,DOM 0級事件註冊(利用DOM元素行內事件屬性onclick註冊事件回調),DOM 1級事件註冊(利用DOM元素對象的onclick API 在外部註冊事件回調),DOM 2級事件註冊(利用利用DOM元素對象的addEventListner/attachEvent API 在外部註冊事件回調)。這裏性能優化的建議就是利用DOM2級在目標DOM的父標籤(大部分框架是在body標籤統一註冊事件監聽)註冊回調,收攏事件監聽入口同時節約了DOM節點引用開銷。

三、避開Cookie性能bug

Cookie是前端做爲先後臺登陸態校驗最一般用的緩存方案,但鑑於瀏覽器在每次都會往同域的任何資源的http請求中自動帶上cookie信息的狀況,這裏有必要進行優化一下,由於像css、js、image這些資源請求是不須要cookie信息的,會無故形成請求帶寬的浪費(想象一下咱們的cookie大小假設爲10K,100個請求就是近1M的大小,高併發下以咱們現行網絡帶寬也是蠻大的一筆負擔了)。Cookie free性能優化方案的處理方式是CDN異域靜態資源服務器部署咱們的前端css、js、image資源。

以本身目前負責的香港跨境匯款爲例

圖片描述

頁面路徑下的資源的請求:

圖片描述

CDN資源加載的請求:

圖片描述

經過對比CDN分開部署的資源請求並無帶上cookie信息。

四、突破瀏覽器併發鏈接限制

瀏覽器針對domain,而非頁面page作併發鏈接限制的特性,domain hash的技術優化方案的處理方式是將資源劃分域分開部署,但由於過多的域劃分會增長多餘的DNS開銷,這裏通行的數量是3個之內。目前咱們的港菲匯款業務只有兩個域名分開部署,一個主站,一個CDN,我我的建議能夠將CDN中的圖片資源再單獨再分一個域名部署會更好些,爲何單獨把圖片抽出來,後面會講到。

五、利用GPU硬件加速瀏覽器渲染

針對一些界面渲染過程比較耗時的狀況下,能夠利用CSS3屬性開啓GPU來加速渲染咱們的DOM,開啓很簡單通常我是用-webkit-transform:translateZ(0)假3D屬性來喚起系統GPU加速渲染功能,關於爲何會這樣,我這裏作個簡單的解釋:

對於咱們的瀏覽器而言,拿到咱們的html文本串開始按順序解析成DOM樹,並與同步解析出來的CSS匹配生成渲染樹(跟DOM樹的節點不是一一對應,好比display:none的節點就不會插入渲染樹)


圖片來源:https://segmentfault.com/a/11...

瀏覽器將渲染樹的節點用一個圖層表示,這樣層層疊加在一塊兒生成layout,有點像ps的圖層疊加的概念(能夠經過火狐瀏覽器開發者工具3維展現更直觀),通常狀況下對節點的任何涉及尺寸的改變都會引發layout的重排重繪(重排和重繪是形成瀏覽器渲染的最大性能損耗的因素),但有種開小竈的狀況Composite Layers(複合圖層)直接交給咱們GPU中單獨的合成器進程處理,自身變化不會引發其餘層的位置變化,不會引發重排重繪。tranform 3d屬性是能夠悄悄的告訴咱們的瀏覽器把元素解析做爲複合圖層交給單獨進程去處理的。

注:這裏有個原則,不能濫用咱們的加速,由於過多開啓硬件加速會消耗更多的用戶內存空間,也會比較耗電,通常針對css3動畫建議開啓

2、Http維度

一、減小http請求數量

a、通行解決方案

  • css、script合併:gulp、webpack都可以很簡單的經過任務腳本的方式去自動化解決,目前咱們團隊是用咱們自研的前端構建工具配合咱們的Dust庫作的發佈前的資源打包任務,核心就是用的gulp。

  • css sprites雪碧圖:將網站經常使用的一些小圖片整合到一張大圖上來,樣式裏面經過background-position二維座標定位找到本身的圖片。這裏有個原則,通常是將網站複用率較高的,不太容易變更的圖標和圖片,好比按鈕、平鋪背景小圖片等。

  • font-icon字體圖標:字體圖標庫的使用,是一個很是有創新的方式,由於是矢量的,解決位圖像素放大變虛的問題,體驗很好,相比一樣矢量的SVG來講使用更簡單,一個css的font-family就能夠像平時設置字體同樣使用,淘寶是國內這方面的先行者,有本身的一套很開放的矢量圖標庫平臺。淘寶自身的許多小圖標都是用的字體圖標來展現的。

圖片描述

圖片描述

  • 圖片base64編碼傳輸:圖片base64編碼後,可讓瀏覽器減小自身的一次http請求,但由於自身的一些缺陷,不能濫用(即便一個很小的圖片編碼後都會有一大串字符,增長了咱們CSS體積,性能不降反升),個人建議是針對那些全站通用或者體積很小很差整合到雪碧圖裏面的圖標進行編碼,固然還有不少不一樣的場景你們本身權衡。

  • 圖片延時加載:主要是爲了減小首屏一次性圖片的加載量。具體作法是給圖片或者標籤設置一個私有行內屬性data-image(固然能夠本身隨便定義)存放目標圖片地址信息,監聽瀏覽器的滾動事件,標籤到了瀏覽器可視區域就將圖片地址放入圖片的src屬性中或者做爲標籤的樣式的背景圖片中展現。淘寶首頁的作法是用一個div來作延時圖片加載,經過背景圖片來展現最後的圖片。

圖片展現前:

圖片描述

圖片展現後:

圖片描述

b、緩存機制

  • 協議緩存方案:利用http緩存協議頭cache-control作304緩存,或者更精確的ETAG設置依據資源的修改時間來設置緩存方案。但目前更有效或者極端的作法是利用max-expire-time,設置資源的最大緩存時間假設爲1年的長緩存,更新採用非覆蓋式更新的方式是目前大公司通行的作法。這樣每次資源請求的時候都是隻從客戶端緩存讀取(status:200,size:from
    cache),而不是還要跑一次http請求到服務器端拿到304狀態。仍是以一張淘寶首頁圖片長緩存的截圖爲例:

  • appCache應用緩存方案:離線應用緩存是h5提供一個比較有效的離線應用方案,利用navigator.online
    、window.applicationCache對象、服務器.appcache(之前是.manifest)配置文件保證在脫機下的移動web應用照常能用,若是要作數據的離線還要加上window.localStorage作離線數據的保存。這裏簡單說一下接入離線應用須要的幾個步驟:

一、給須要作離線緩存的頁面html標籤設定manifest屬性,指定緩存的配置文件 cache.appcahe(能夠設定任何擴展名,只要在服務器端配置mime-type爲text/cache-manifest就行)。

二、建立上一步指定的cache.appcache配置文件,按如下截圖說明來配置資源

圖片描述

三、在服務器端配置配置文件的擴展名映射的mime-type爲text/cache-manifest

圖片描述

appcache離線方案詬病太多,目前接入的很少,有種慢慢變棄兒的趨勢,這裏提出來讓你們權衡

  • **PWA(Progressive Web
    Apps)方案**:谷歌提出的一套全新的離線web方案,利用manifest.json配置文件、window.serviceWorker對象來實現類原生app體驗的離線應用方案,能夠說是瀏覽器應用緩存的一個脫胎升級方案(鑑於文章篇幅,這裏不作介紹了)。

二、減輕http數據請求大小

a、通行解決方案

  • css、script、圖片壓縮:這些能夠gulp或者webpack自動化腳本里面定義腳本任務來完成。

  • 服務器開啓gzip壓縮:通常如今服務器都有開啓Gzip壓縮,壓縮率一般都是30%以上,效果仍是不錯的。

原圖:

圖片描述

Gzip壓縮後:

圖片描述

  • 圖片服務器動態響應方案:這個方案對應上面宿主環境維度domain hash單獨出來一個獨立域名部署圖片資源的方案介紹。圖片資源是網站請求資源中一個很是大頭的開銷,之前你們能夠在靜態資源服務器中建個image目錄存放就完事,隨着網站服務發展,圖片不只面臨多樣化、高併發帶來的壓力,在移動端wap站點中更是要針對不一樣的分辨率屏幕下圖片尺寸動態適配的場景覺得了節省帶寬的需求。圖片服務器的單獨架構有必定的複雜度(若是考慮到高併發下的容災、緩存機制的話不亞於一個大型web網站集羣的搭建,這裏有篇文章推薦你們閱讀),這裏只討論一下其中負責切圖服務部分的服務器(簡稱切圖服務器)功能,切圖服務器對外提供一個restful的url調用,好比http://www.xxx.com/xxx圖片路...「xxx圖片路徑」下的xxx圖片等比壓縮成130*120的圖片尺寸並返回,這塊服務可使用咱們前端比較熟悉的node建立,固然也能夠用PHP來提供。

b、頁面切片預加載方案

性能優化靜態資源維度最後一塊內容就是針對頁面,如何儘早輸出頁面模塊,減小留白時間是一個思考點。facebook應用的BigPipe方案是個很不錯的借鑑思想,還有淘寶也有首頁作了相應的切片方案,對頁面合理的分塊,在服務器和客戶端創建某種對應機制,讓各個頁面塊並行的在服務器端拼接完成並吐出來,目前我對這塊沒有太深的瞭解,這裏只是提出bigPipe的方案供你們參考。

3、TCP維度

TCP鏈接中的3次握手、慢啓動的一些特性註定了鏈接通道的利用效率成爲制約性能的一個很大的因素。由於http是基於TCP的應用協議,TCP層維度考慮還得從http幾個版本的發展歷史來看:

  • http1.0時期:tcp鏈接是基於一種單通道順序等待請求響應方式(客戶端每發一個請求都要從新創建鏈接),特定歷史背景下產生的,低效率很難跟上時代發展,99年在1.0基礎上修訂出1.1版本,並沿用至今。

  • http1.1時期:在請求頭信息加入keep Alive保持鏈接的必定活性(固然也加入了100
    Status節約帶寬、cache特性等),容許在一個鏈接通道生命期內重複發送不一樣的應用請求,必定程度上減輕了鏈接資源利用效率問題,但當用戶瀏覽網頁時間大於鏈接活性週期再次請求的時候仍然要從新創建這些請求,在大型科技公司對高併發高可用性下資源高效利用的背景下,1.1版本仍是難知足大公司對高性能條件下網絡資源的高效率利用的要求。

谷歌(叒是谷歌,牛逼)率先在09年基於TCP開發出全新SPDY應用協議,解決了多路複用請求優化、服務器推送的痛點問題,也爲後面http2.0的推出奠基了基礎。
咱們能夠作的優化:減小一些沒必要要的請求(掃除404死鏈接、304請求用咱們的長緩存機制)去優化,儘可能減小一些沒必要要的鏈接請求數。

4、代碼實現

鑑於js語言自己的靈活性,以及每一個人的開發習慣,很難有很好的一個方式去驗證開發者的代碼實現的效率(目前更多的是用打點測速的方式去監控代碼的執行時間),更多的是一種建議,你們有更好的建議能夠提出來分享。

  • 單線程限制:利用異步回調&多線程API突破js語言單線程帶來的內存開銷利用不充分的問題,現有能夠利用的一些異步方式的回調均可以嘗試利用好比settimeout,setinterval,requestAnimationframe(推薦用它),多線程的API方式有WebWork。這裏推薦日本的一個開發者開發的一個多線程前端庫Concurrent.Thread.js(它是做者用setTimeout和setInterval來模擬的多線程,能夠自行網上搜索瞭解)。

  • 重點優化代碼中的循環結構體:就代碼自己而言影響執行效率的大部分是循環體結構和算法設計不合理致使的時間複雜度和空間複雜度的增長。這裏放兩段實際項目中的代碼截圖來對比:

實例功能須要:實現輸入框每4個字符一個空格隔開的效果

低效實現方式,用for循環:

圖片描述

改進後的方式:

圖片描述

  • 合理使用設計模式優化代碼結構:設計模式的合理利用(不能濫用)能夠起到內存優化提升執行效率效果,好比單例和簡單工廠在建立xhr請求對象中的利用:建立一個簡單工廠向外面提供xhr對象,工廠內部用閉包開闢一個數組隊列單例用來存放xhr對象,當調用者須要xhr對象時工廠就從隊列裏取出readyState狀態爲空閒(0/4)的xhr對象不然從新建立並放入隊列中。在有大量ajax對象請求的應用下能夠最大限度節約建立xhr消耗的內存開銷,這裏用個簡圖來描述一下思路:

  • 其餘一些優化建議:好比減小js頻繁操做dom節點的次數(這個原本想放在宿主環境維度)減小瀏覽器的重排重繪;好比針對dom標籤對象(主要是針對ie下dom對象的引用會被GC回收遺漏的問題)、閉包內部的引用類型的變量用完事後記得要及時釋放,避免形成內存泄漏。

5、產品交互邏輯

性能優化通常都是從技術角度去入手,但咱們的目標之一「讓用戶簡單易用」也是性能優化的一環。當技術性能缺陷難於避免的時候,做爲前端交互實現的執行者,更應該配合產品和交互設計師提出一個咱們認爲更好的交互邏輯體驗方案去讓咱們的數據加載不那麼讓用戶有等待的感知,讓咱們的提示更加的人性舒服。(交互設計師更加專業,我這裏不敢班門弄斧)

Web3.0時代的前端展望:

文章最後對Web3.0時代作個本身的猜測,web3.0目前在業內尚未很明肯定義,你們能夠大膽猜測前端行業將來形態。我在這先YY一下,人工智能、大數據普遍應用應該會成爲推進前端進入3.0的時代的最好契機,以此引起的前端新的革命:

  • 瀏覽器成爲一個系統生態(至於哪一個瀏覽器如今很差說,如今谷歌瀏覽器PWA方案提供給前端類app開發方案就有這個趨勢,之後都不須要裝系統了)。前端再也不是數據的搬運工,在web領域頗有可能除了底層維護數據庫的工程師們繼續深耕外(主要切入雲計算領域),其它的均可以轉到前端作瀏覽器系統生態(哎媽呀,有種翻身農奴作主人的感受O(∩_∩)O~~)。

  • 傳統的html語義性的超文本標記語言已經很難承載更多的信息化數據,html5繼續深度發展,再也不只是瀏覽器專用的標記語言,可能會成爲跨平臺標準的信息表示層,信息表示多樣化史無前例,可能到時候不叫html了。

  • http2.0成爲一個普遍試用的標準,並進一步深化,安全校驗層作到相似區塊鏈去中心化的思路,作到極致安全(https估計能夠下崗了)。

  • js成爲跨平臺公認的標準實現語言(目前前端跨平臺基礎形態已經有了),隨着Es6的普遍推廣和深度改進,可能就不會像如今這麼靈活,更加像一個合格的標準「高級」腳本語言。

  • (以上猜測純屬我的理解,沒有權威認證,你們能夠暢所欲言)

結語:剛來鵝廠不久,做爲前端攻城獅進入到國內頂尖互聯網公司感到驕傲,性能優化是一個永恆的話題,每一個階段都有聊不完的性能優化的話題,我將我這些年的一些不成文的理解整理了一下,但願對你們有點幫助。第一次發文章,有錯誤的地方請你們指點一二。

相關閱讀

Web 前端性能優化 : 如何有效提高靜態文件的加載速度
使用 Skeleton Screen 提高用戶感知體驗
盒子端 CSS 動畫性能提高研究

此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處
原文連接:https://cloud.tencent.com/com...

相關文章
相關標籤/搜索