前端性能優化

前言

前端性能優化對於一個前端來說比如是「演員的自我修養」,想作一名好的前端,必定要懂如何性能優化。另外性能優化基本上是面試必問的一個問題了,這一塊能回答的很漂亮,無疑是一個加分項。javascript

感知性能

對於用戶來講,用戶的感知性能纔是最重要的,簡單講,就是讓用戶感受你的網站訪問很快,而且感知性能沒有衡量標準。css

若是一個頁面的加載時間很長,咱們也能夠經過一些方式讓用戶以爲沒有那麼慢。html

loading

最基礎的菊花等待~前端

骨架屏

能夠參考antd-design的骨架屏java

客觀性能

對於開發者來講,性能指標是能夠客觀度量的,咱們能夠經過一些手段來優化 Web 性能,使這些度量指標達到開發者設定的標準。node

客觀性能是指,從用戶輸入url開始,到下載、解析和執行全部資源以及最終繪製的整個過程的時間度量。webpack

瀏覽器打開網頁的過程web

1.瀏覽器對URL進行DNS解析面試

2.瀏覽器與服務器進行TCP鏈接npm

3.瀏覽器發出HTTP請求

4.服務器返回HTTP響應

5.瀏覽器進行頁面渲染

性能指標

image.png Google提出的網站用戶體驗的三大核心指標

LCP、FID、CLS

LCP 表明了頁面的速度指標

FID 表明了頁面的交互體驗指標

CLS 表明了頁面的穩定指標

經常使用的性能優化方法

減小請求次數

資源合併

使用打包工具,對js、css資源進行打包,避免文件過多

使用雪碧圖

圖片走cdn等

緩存

HTTP Cache

強緩存

Expires http1.0的產物,如今已經不用

Cache-Control

針對瀏覽器和服務器時間不一樣步,加入了新的緩存方案;此次服務器不是直接告訴瀏覽器過時時間,而是告訴一個相對時間Cache-Control=10秒,意思是10秒內,直接使用瀏覽器緩存

app.get('/demo.js',(req, res)=>{
    let jsPath = path.resolve(__dirname,'./static/js/demo.js');
    let cont = fs.readFileSync(jsPath);
    res.setHeader('Cache-Control', 'public,max-age=120') //2分鐘
    res.end(cont)
})
複製代碼

強緩存的特色是不須要詢問服務器

協商緩存

強制緩存的弊端很明顯,即每次都是根據時間來判斷緩存是否過時;可是當到達過時時間後,若是文件沒有改動,再次去獲取文件就有點浪費服務器的資源了

協商緩存是請求服務器後,服務器來判斷是返回新的資源,仍是告訴瀏覽器使用舊的資源

有根據最後修改時間、和文件內容是否改動兩種協商方法

Last-Modified和If-Modified-Since

只能精確到秒,若是一秒內屢次修改,就感知不到

確實修改後,但內容沒變,也會新請求

ETag和If-None-Match

解決文件修改時間不精確帶來的問題,只有當文件內容改變時,ETag才改變

image.png 服務器讀取磁盤文件demo.js,返給瀏覽器,同時帶上文件上次修改時間 Last-Modified(GMT標準格式)

app.get('/demo.js',(req, res)=>{
    let jsPath = path.resolve(__dirname,'./static/js/demo.js')
    let cont = fs.readFileSync(jsPath);
    let status = fs.statSync(jsPath)

    let lastModified = status.mtime.toUTCString()
    if(lastModified === req.headers['if-modified-since']){
        res.writeHead(304, 'Not Modified')
        res.end()
    } else {
        res.setHeader('Cache-Control', 'public,max-age=5')
        res.setHeader('Last-Modified', lastModified)
        res.writeHead(200, 'OK')
        res.end(cont)
    }
})
複製代碼

服務器讀取磁盤文件demo.js,返給瀏覽器,同時帶上文件的惟一標識ETag

Memory Cache & Disk Cache

他們是配合http緩存的。 memory cache命中最快,可是它週期較短,base64的圖片,較小的js和css可以有較大概率被寫進內存,這沒有肯定的定論。 其餘較大的js、css和圖片等會被直接寫進硬盤,進行緩存。

存儲

cookie

最大4K,存儲一些用戶登陸狀態

webStorage

分爲sessionStorage和localStorage,大小在5-10M,鍵值對存儲,區別是生命週期不一樣,sessionStorage在tab關閉後,就不存在了,localStorage永久存在,除非主動刪除。

減小請求體積

資源壓縮

Gzip

傳輸的時候能夠在服務器端開啓gzip壓縮,能夠有效減小傳輸文件的大小,能夠在響應頭content-encoding: gzip中看到。

代碼壓縮

使用一些代碼壓縮工具,刪除掉無用的註釋、空行和縮減名稱等操做來減小文件體積。

圖片壓縮

圖片是網頁上佔用不少流量的一種資源。若是在圖片損失一些顏色和像素的狀況下並不會對用戶體驗有太大影響,那麼就應該對圖片進行壓縮。

圖片壓縮

PNG無損格式,壓縮率通常,支持透明背景,經常使用於透明圖片或者Icon等。

JPG有損格式,壓縮率較好,經常使用於複雜的大圖,不支持透明背景。

SVG矢量圖形,可編程。在各分辨率下不失真,可是渲染複雜圖形較消耗性能。經常使用於簡單圖形。

WEBP無損格式,相較於PNG和JPG來講,壓縮率更好,同時支持透明背景。惟一的缺點是兼容性很差。可用於兼容性好的瀏覽器,用JPG和PNG作回退機制。

服務器發送HTTP相應

減小響應時間

利用CDN(高流量大併發狀況下)

cdn全稱content delivery network。它是依靠部署在各地區的邊緣服務器,達到用戶就近獲取內容,下降網絡擁塞,提升用戶訪問速度和命中率的目的。它主要的關鍵技術是內容存儲和分發技術。

下降頁面初始渲染時間

預渲染

將瀏覽器解析 javascript 動態渲染頁面的這部分工做,在打包階段就完成了,(只構建了靜態數據)換個說法在構建過程當中,webpack 經過使用 prerender-spa-plugin 插件生成靜態結構的 html。

服務器渲染(SSR)

CSR 項目的 TTFP(Time To First Page)時間比較長,參考以前的圖例,在 CSR 的頁面渲染流程中,首先要加載 HTML 文件,以後要下載頁面所需的 JavaScript 文件,而後 JavaScript 文件渲染生成頁面。在這個渲染過程當中至少涉及到兩個 HTTP 請求週期

node作爲中間層,讓 React 代碼在服務器端先執行一次,使得用戶下載的 HTML 已經包含了全部的頁面展現內容,這樣,頁面展現的過程只須要經歷一個 HTTP 請求週期

同時,因爲 HTML 中已經包含了網頁的全部內容,因此網頁的 SEO 效果也會變的很是好。以後,咱們讓 React 代碼在客戶端再次執行,爲 HTML 網頁中的內容添加數據及事件的綁定,頁面就具有了 React 的各類交互能力。

頁面渲染

減小阻塞

js阻塞

當html解析遇到js會先下載和執行js文件,這是爲了防止js操做了dom等狀況的發生。但咱們做爲操做者,能夠人爲的指定,那些元素能夠延遲加載。

爲script標籤指定 async 或 defer來延遲腳本。

async表示js不會阻塞,並行執行,但會在下載完成後馬上執行,誰先加載好誰執行

defer則會在下載完成而且整個文檔解析完成、DOMContentLoaded事件被觸發前開始執行,按照順序執行

css阻塞

css會阻塞html進行渲染,可是爲了界面沒有任何樣式的展示在用戶面前,所以咱們須要將css提早

減小渲染次數

避免迴流和重繪

迴流又稱爲重排,即經過某種手段改變了元素的位置大小等信息,致使瀏覽器須要從新計算和渲染的過程。而重繪只是被改變了樣式如背景和顏色等。

不管是哪種,都會耗費性能,因此咱們要避免進行循環操做。

減小渲染節點數量

懶加載

對於一些不在用戶視圖內的元素,咱們能夠在展現的時候先不進行渲染,直到該元素出如今了視圖內再進行渲染。

懶加載包括對圖片或者dom元素的加載和渲染

提升渲染效率

減小DOM節點的操做

瀏覽器的渲染引擎和js引擎是分離的,能夠想象在js引擎和渲染引擎之間進行」跨界交流「並不簡單,這個開銷很大,全部咱們要儘可能避免這種操做。

下降選擇器的複雜性

.box:nth-last-child(-n+1) .title { /* styles */ }

瀏覽器計算此結果可能須要大量的時間,但咱們能夠把選擇器的預期行爲更改成一個類:

.final-box-title { /* styles */ }

避免強制同步佈局和佈局抖動

瀏覽器每次佈局計算時幾乎老是會做用到整個DOM,若是有大量元素,將會須要很長時間才能計算出全部元素尺寸和位置。

因此咱們應該避免在運行時動態的修改集合屬性(寬高)。若是沒法避免,優先使用Flexbox

內聯首屏關鍵css(Critical CSS)

瀏覽器在將咱們的頁面呈現給用戶以前必定要先完成頁面引用到的CSS文件的下載和解析(download and parse),因此link標籤連接的CSS資源是渲染阻塞的(render-blocking)。若是CSS文件很是大或者網絡的情況不好,渲染阻塞的CSS會嚴重影響用戶體驗。針對這個問題,社區有一種優化方案就是將一些重要的CSS代碼(Critical CSS)直接放在頭部的style標籤內,其他的CSS代碼再進行異步加載,這樣瀏覽器在解析完HTML後就能夠直接渲染頁面了。

/* critical CSS */ ...body goes here

能夠直接npm I criticalcss

那麼如何定義Critical CSS呢?放在head標籤內的CSS固然是越少越好,由於太多的內容會加大html的體積,因此咱們通常把用戶須要在首屏看到的(above the fold)頁面要用到的最少CSS提取爲Critical CSS。因爲頁面在不一樣的設備上展現的效果不一樣,對應着的Critical CSS內容也會有所差異,所以Critical CSS的提取是一個十分複雜的過程,雖然社區有不少對應的工具但是效果都差強人意。CSS-in-JS卻能夠很好地支持Critical CSS的生成。在CSS-in-JS中,因爲CSS是和組件綁定在一塊兒的,只有當組件掛載到頁面上的時候,它們的CSS樣式纔會被插入到頁面的style標籤內,因此很容易就能夠知道哪些CSS樣式須要在首屏渲染的時候發送給客戶端,再配合打包工具的Code Splitting功能,能夠將加載到頁面的代碼最小化,從而達到Critical CSS的效果。

換句話來講,CSS-in-JS經過增長一點加載的JS體積就能夠避免另外發一次請求來獲取其它的CSS文件。並且一些CSS-in-JS的實現(例如styled-components)對Critical CSS是自動支持的。

相關文章
相關標籤/搜索