前端性能優化一篇就夠了

本文將從如下幾個維度來分析優化

  • 構建優化
  • 靜態資源優化
  • 網絡層優化
  • 緩存
  • 渲染層優化

構建優化

1. tree shaking

構建後的js代碼只包含被引用並被執行的模塊,而不被引用或不被執行的模塊會被刪除,以起到減包的做用。javascript

webpack 在 mode爲production下默認執行tree shaking,不設置mode默認爲productioncss

注意項:tree shaking只會檢測 ES2015 模塊語法(即 import 和 export)。更多細節html

1.1 webpack的tree shaking存在的問題

webpack的tree shaking至今實現的功能並不完美,凡有反作用的模塊,tree-shaking並不起做用前端

例子🌰:vue

// index.js
import { func2, func3 } from './module-a'
func2()
複製代碼
// module-a.js
import lodash from 'lodash-es'
export * from './module-b'
export const func1 = function(value) {
  return lodash.isArray(value)
}
export const func2 = function() {
  console.log('這是func2')
  return 123123
}
複製代碼
// module-b.js
export const func3 = () => {
  console.log('B模塊的func3方法')
}
複製代碼

圖片
經過打包的數據咱們能夠看到在index.js中咱們並無使用func1,可是在bundle中仍是將lodash打包了進去,這就是由於func1函數的反作用,webpack自身的tree shaking並無檢測到這裏有沒有必要的模塊。

1.2 webpack-deep-scope-plugin優化webpack的tree shaking(存在小問題)

這個插件主要用於填充webpack自身Tree-shaking的不足,經過做用域分析來消除無用的代碼。原理請看java

const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin')
  .default
...
plugins: [
  new WebpackDeepScopeAnalysisPlugin(),
],
複製代碼

圖片
能夠看到bundle的大小減小到了80+k,經過查看bundle的代碼能夠看出lodash已經被徹底刪除

2. css的tree shaking

對於css的tree shaking 則使用 purgecss-webpack-plugin 和 mini-css-extract-plugin 來實現react

在css中添加如下代碼:webpack

.red {
  color: red
}

.blue {
  color: blue
}
複製代碼

而後在js文件中引用:nginx

import './index.css'

var div = createElement('div')
div.styles.class = 'red'

console.log(div);
複製代碼

理想狀況下,咱們但願打包後的文件中只含**.red而不含未使用的.blue**,配置以下:git

const TerserJSPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgecssPlugin = require('purgecss-webpack-plugin')
optimization: {
  // 壓縮js、css
  minimizer: [
    new TerserJSPlugin({}),
    new OptimizeCSSAssetsPlugin({})
  ],
},
module: {
...
    use: [
      MiniCssExtractPlugin.loader, 
      'css-loader',
    ]
}
plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
    }),
    // css tree shaking
    new PurgecssPlugin({
      paths: glob.sync(`${path.join(__dirname, '/src/**/*')}`,  { nodir: true }),
    }),
]
複製代碼

mini-css-extract-plugin會將css打包爲一個單獨的文件,這裏咱們使用插件purgecss-webpack-plugin幫助咱們分析過濾未引用的css,最終結果以下(壓縮有關的插件和配置參考第6小節):

圖片

3. scope hoisting (做用域提高)

Scope Hoisting 它可讓webpack打包出來的代碼文件更小,運行更快,它能夠被稱做爲 "做用域提高"。

啓用 Scope Hoisting的優勢以下:

  1. 代碼體積會變小,由於函數聲明語句會產生大量代碼。

  2. 代碼在運行時由於建立的函數做用域減小了,因此內存開銷就變小了。

plugins: [
  // 開啓 Scope Hoisting 功能
  new webpack.optimize.ModuleConcatenationPlugin()
]
複製代碼

在 mode production 狀況下默認開啓

咱們在開發環境進行驗證,這裏有兩個文件:

// index.js
import a from './module-a'
console.log(a);
// module-a.js
export default 'module-a'
複製代碼

當未啓動Scope Hoisting時,按照文件模塊打包,生成多個函數做用域,結果以下:

圖片

啓用時則會將兩個模塊預編譯到同一個模塊中:

圖片

4. code spiltting(代碼分割)

  1. 第三方類庫單獨打包: 因爲第三方類庫的內容基本不會改變, 能夠將其與業務代碼分離出來, 這樣就能夠最大化的利用瀏覽器的緩存機制, 減小請求.
  2. 按需加載: Webpack支持定義分割點, 經過require.ensure/import()進行按需加載

4.1 使用entry多入口分離代碼

e.g.

// index.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'index'}))
// module.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'module'}))
複製代碼

這裏有兩個文件中都引用了lodash這個第三方包,直接打包的話兩個文件中都會包含lodash的代碼:

圖片

可使用optimization.splitChunks來清除重複項:

optimization: {
  splitChunks: {
    chunks: 'all'
  }
}
複製代碼

清除重複後會將lodash生成到一個單獨的chunk中:

圖片

4.2 動態導入

當涉及到動態代碼拆分時,可使用**import()**動態加載,或者webpack特有的require.ensure

// index.js
function getLodash() {
  return import(/* webpackChunkName: "lodash" */'lodash').then(({ default: _ }) => {
    return _.cloneDeep({a: 'index'})
  }).catch(e => 'error occurred')
}

getLodash().then(res => {
  console.log(res);
})
複製代碼

這裏咱們import()動態加載lodash,而且利用註釋設置了chunkname,能夠看到將動態引入的lodash單獨拆分爲一個文件:

圖片

更多詳細介紹能夠參考Code SplittingSplitChunksPlugin

5. 優化公共資源包

將框架包 vue、react、ui框架、sdk等框架包抽離放入cdn

externals: {
 vue: "Vue",
}
複製代碼

6. 代碼壓縮

html 壓縮 HtmlWebpackPlugin

js 壓縮 UglifyJsPluginTerserWebpackPlugin

css 壓縮 css-loader 內置了壓縮,配合ExtractTextPluginMiniCssExtractPlugin

給css加上hash

6.1 html壓縮

e.g.

const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
  new HtmlWebpackPlugin({
    minify: {
      collapseWhitespace: true,
      removeComments: true,
    },
  }),
]
複製代碼

能夠看到html文件實現了壓縮:

圖片

HtmlWebpackPlugin 3.x 中,minify爲true時默認爲{},此時須要手動配置,在4.x中則爲

{
  collapseWhitespace: true,
  removeComments: true,
  removeRedundantAttributes: true,
  removeScriptTypeAttributes: true,
  removeStyleLinkTypeAttributes: true,
  useShortDoctype: true
}
複製代碼

詳細參見:github.com/jantimon/ht… 關於具體配置項能夠參考html-minifier

6.2 js壓縮

webpack 4.x 中默認使用TerserWebpackPlugin進行js壓縮,原有的UglifyjsWebpackPlugin不推薦使用

關於TerserWebpackPlugin和UglifyJsPlugin,你須要知道如下幾點:

  • TerserWebpackPlugin底層使用的是terser
  • UglifyJsPlugin底層使用的是uglify-js
  • uglify-js 不支持es6,須要另外配置 uglify-es,可是uglify-es再也不維護更新
  • terser 則保留了uglify-js@3和uglify-es的api和兼容性

在3.x中開發環境下配置以下:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
plugins: [
  new UglifyJsPlugin(),
]
複製代碼

4.x開發環境中開啓,production下minimize默認爲true:

optimization: {
  minimize: true,
}
複製代碼

壓縮前:

圖片

壓縮後:

圖片

還能夠在minimizer中新增TerserWebpackPlugin實例覆蓋默認壓縮:

  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        test: /\.js(\?.*)?$/i,
      }),
    ],
  },
複製代碼

更多配置項

6.3 壓縮css

將來在webpack5中將會引入內置的css minimizer,在4.x中須要手動配置 optimize-css-assets-webpack-plugin

手動配置 minimizer 會覆蓋掉默認配置,所以須要補上TerserJSPlugin壓縮js

MiniCssExtractPlugin則會提取css到單獨的文件,配置以下:

const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
optimization: {
  minimizer: [
    // 壓縮js
    new TerserJSPlugin({}),
    // 壓縮css
    new OptimizeCSSAssetsPlugin({})
  ]
},
plugins: [
  new MiniCssExtractPlugin({
    filename: "[name].css",
    chunkFilename: "[id].css"
  })
],
module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        "css-loader"
      ]
    }
  ]
}
複製代碼

7. 使用preload與prefetch

在import動態加載中配置preload和prefetch

預加載:

是一種 resource hint,用來指定頁面加載後很快會被用到的資源,因此在頁面加載的過程當中,咱們但願在瀏覽器開始主體渲染以前儘早 preload。
import(/* webpackPreload: true */ 'Modal');
複製代碼

預取:

是一種 resource hint,用來告訴瀏覽器在頁面加載完成後,利用空閒時間提早獲取用戶將來可能會訪問的內容。
import(/* webpackPrefetch: true */ 'Modal');
複製代碼

兩者的區別:

  • preload chunk 會在父 chunk 加載時,以並行方式開始加載。prefetch chunk 會在父 chunk 加載結束後開始加載。
  • preload chunk 具備中等優先級,並當即下載。prefetch chunk 在瀏覽器閒置時下載。
  • preload chunk 會在父 chunk 中當即請求,用於當下時刻。prefetch chunk 會用於將來的某個時刻。
  • 瀏覽器支持程度不一樣。

preload:

圖片

prefetch:

圖片

更多資料

8. chunk名稱

將hash替換爲chunkhash,這樣當chunk不變時,緩存依然有效

使用Name而不是id

每一個 module.id 會基於默認的解析順序(resolve order)進行增量。也就是說,當解析順序發生變化,ID 也會隨之改變

webpack緩存

靜態資源優化

1.Gzip -> Brotli

Brotli 壓縮算法具備多個特色,最典型的是如下 2 個:

  • 針對常見的 Web 資源內容,Brotli 的性能相比 Gzip 提升了 17-25%;
  • 當 Brotli 壓縮級別爲 1 時,壓縮率比 Gzip 壓縮等級爲 9(最高)時還要高;

圖片

brotil的支持狀況 可查看caniuse

1.1 不壓縮的狀況

圖片

1.2 gzip 9級壓縮

圖片

1.3 brotil 11級壓縮

圖片

1.4 選擇合適的壓縮時機

動態壓縮即時發生。用戶發出請求,壓縮內容(當用戶等待時)而且提供壓縮內容。

靜態壓縮在用戶請求以前在磁盤上壓縮資產的時間。當用戶請求資產時,不會發生壓縮。預壓縮資產只是從磁盤提供。

webpack提供的壓縮方式compression-webpack-pluginbrotli-webpack-plugin

gzip:

const CompressionWebpackPlugin = require('compression-webpack-plugin')
new CompressionWebpackPlugin({
  asset: '[path].gz[query]',
  algorithm: 'gzip',
  test: /\.(js|css|html|svg)$/,
  threshold: 10240,
  minRatio: 0.8
})
複製代碼

brotli:

const BrotliPlugin = require('brotli-webpack-plugin')
new BrotliPlugin({
  asset: '[path].br[query]',
  test: /\.(js|css|html|svg)$/,
  threshold: 10240,
  minRatio: 0.8
})
複製代碼

1.5 nginx.conf

gzip  on;
gzip_vary               on;
gzip_min_length         1024;
gzip_buffers            128 32k;
gzip_comp_level         9;
gzip_http_version       1.1;
gzip_proxied            expired no-cache no-store private auth;
gzip_types              text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;

brotli on;
brotli_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli_static off;
brotli_comp_level 11;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_min_length 20;
複製代碼

2.圖片優化

2.1 選擇合適的圖片格式

圖片

2.2 圖片壓縮

壓縮工具:

也能夠經過webpack壓縮 imagemin-webpack-plugin

tinypng

圖片

imagemin

依賴:url-loader、file-loader、imagemin-webpack-plugin

圖片

const path = require('path')
const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              name: '[name].[hash:8].[ext]',
              outputPath: 'imgs/',
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i }),
  ],
}
複製代碼

不推薦在打包的時候經過webpack壓縮,由於這樣會大量增長打包的時間,而且imagemin這個插件的壓縮能力遠不如tinypng

2.3 大的背景圖採用圖片切分(h2)

將過大的背景圖切分紅多張小圖,經過html的方式拼到一塊兒。

利用瀏覽器的併發加載(h2)

2.4 小圖base64/字體/雪碧圖

網絡請求 緩存 靈活性 其餘
雪碧圖 1.放大失真 2.同一圖標,不一樣顏色,會有重複部分 3.只用幾張也須要加載整張圖
iconfont 1.矢量放大不失真 2.跟普通字體無異,能夠設置size,color,opacity, 等 3.文件小
base64 1.增長額外的css、js大小 2.增長圖片資源的大小

2.5 響應式圖片

圖片

響應式圖片不只僅指圖片的排版和佈局,還包括根據設備大小加載不一樣的圖片。

image:

經過設置image標籤上的srcset告知瀏覽器在不一樣屏幕寬度下加載不一樣的圖片

經過設置image標籤上的sizes來設置圖片的尺寸臨界點,明肯定義了圖片在不一樣的media conditions下應該顯示的尺寸。

picture:

瀏覽器會遍歷 中的,直到找到一個知足當前環境,而後將該中的srcset設置到

響應式圖片段點生成器

2.5 使用體積小、可緩存的favicon.ico

favicon.ico通常存放在網站根目錄下,不管是否在頁面中設置,瀏覽器都會嘗試請求這個文件。

因此確保這個圖標:

  • 存在(避免 404);
  • 儘可能小,最好小於 1K;
  • 設置較長的過時時間。
    圖片

3.使用cdn

在不一樣地域的用戶請求資源(訪問網站)的響應速度具備很大的差別,爲了提升用戶體驗,咱們在用戶和服務器中間加了一層,就是CDN。CDN(Content Delivery Network),它的思想就是將源站的內容分發到最接近用戶的網絡邊緣節點,讓用戶可以就近取得所需的內容,提升用戶訪問的響應速度。

當用戶發起HTTP請求時,經過CDN向邊緣節點服務器發起請求,邊緣節點會檢測當前節點是否具備你想請求的數據,若是沒有就去源站,若是有請求數據就會進一步判斷,這個數據是否在有效期,根據是否過時來決定。

網絡層優化

一次完整的http請求過程

DNS解析(T1) -> 創建TCP鏈接(T2) -> 發送請求(T3) -> 等待服務器返回首字節(TTFB)(T4) ->> 接收數據(T5)

圖片

  • Queueing:請求排隊。
  • Stalled:請求阻塞;
  • Proxy negotiation: 與代理服務器鏈接的時間花費
  • DNS Lookup:dns查詢。
  • Initial connection:創建TCP鏈接的時間,就至關於客戶端從發請求開始到TCP握手結束這一段。
  • SSL(包含於HTTPS鏈接中):完成SSL握手的時間花費。
  • Request sent(發送請求):發送HTTP請求的時間(從第一個字節發出前到最後一個字節發出後的時間)
  • Waiting(TTFB) :請求發出後,到收到響應的第一個字節所花費的時間(Time To First Byte),發送請求完畢到接收請求開始的時間;一般是耗費時間最長的。從發送請求到收到服務器響應的第一字節之間的時間,受到線路、服務器距離等因素的影響。
  • Content Download(下載):收到響應的第一個字節,到接受完最後一個字節的時間,就是下載時間。

影響一個http請求的主要因素

  • 帶寬 - 網速
  • 延遲
    • 瀏覽器阻塞: 以chrome爲例,瀏覽器對於同一個域名,同時只能有6個鏈接,超過這個數的請求就會被阻塞
    • DNS查詢:瀏覽器須要知道目標服務器的 IP 才能創建鏈接。將域名解析爲 IP 的這個系統就是 DNS。這個一般能夠利用DNS緩存結果來達到減小這個時間的目的。
    • TCP鏈接:HTTP 是基於 TCP 協議的,瀏覽器最快也要在第三次握手時才能捎帶 HTTP 請求報文,達到真正的創建鏈接,可是這些鏈接沒法複用會致使每次請求都經歷三次握手和慢啓動。三次握手在高延遲的場景下影響較明顯,慢啓動則對文件類大請求影響較大。

1. 減小阻塞時間

1.1 可能產生阻塞的緣由

  • There are higher priority requests.
  • There are already six TCP connections open for this origin, which is the limit. Applies to HTTP/1.0 and HTTP/1.1 only.
  • The browser is briefly allocating space in the disk cache

1.2 合理的請求合併與拆分(http1.1)

圖片

以chrome爲例,http1.1的併發請求數量最大是6(同一域名下),超出的併發請求必須等待

**對於大資源:**是否合併對於加載時間沒有明顯影響,但拆分資源能夠更好的利用瀏覽器緩存,不會由於某個資源的更新致使全部資源緩存失效,而資源合併後,任一資源的更新都會致使總體資源的緩存失效。另外還能夠利用域名分片技術,將資源拆分部署到不一樣域名下,既能夠分散服務器的壓力,又能夠下降網絡抖動帶來的影響。

**對於小資源:**合併資源每每具備更快的加載速度,但在網絡帶寬情況良好的狀況下,由於提高的時間單位以ms計量,收益能夠忽略。若是網絡延遲很大,服務器響應速度又慢,則能夠帶來必定收益,但在高延遲的網絡場景下,又要注意合併資源後可能帶來網絡往返次數的增長,進而影響到加載時間。

1.3 升級到http2

2. 減小DNS查詢時間

2.1 dns查詢的步驟

瀏覽器對網站第一次的域名DNS解析查找流程依次爲:瀏覽器緩存 -> 系統緩存 -> 本地hosts文件 -> 路由器緩存 -> ISP DNS緩存 -> 遞歸搜索

2.2 服務器增長DNS緩存

如今通常服務器都具有DNS緩存

2.3 dns prefetch

  1. 啓用DNS預解析:

在瀏覽器支持 DNS 預解析的特性時即便不使用該標籤瀏覽器依然會進行預解析。

<meta http-equiv="x-dns-prefetch-control" content="on"> // 關閉 off
複製代碼
  1. 強制查詢特定的主機名:
<link rel="dns-prefetch" href="//domain.com">
複製代碼

圖片

淘寶的預解析

注:dns-prefetch需慎用,多頁面重複DNS預解析會增長重複DNS查詢次數。

須要注意的是,雖然使用 DNS Prefetch 可以加快頁面的解析速度,可是也不能濫用,由於有開發者指出 禁用DNS 預讀取能節省每個月100億的DNS查詢 。

2.4 減小dns查詢

  1. 利用Connection:keep-alive特性創建持久鏈接,能夠在當前鏈接上進行多個請求,無需再進行域名解析
  2. 資源放在同一域名下,利用dns的緩存

3. 減小TCP鏈接時間

  1. 創建持久鏈接(keep-alive)
  2. 開啓OCSP(在線證書狀態協議)
  3. 升級到http2

4. 減小Request請求時間

4.1 減小cookie的使用

每一次的http請求都會默認攜帶上cookie,cookie過大的話會致使傳輸變慢

4.2 cookie隔離

對於一些靜態資源(圖片)的獲取,是不須要用到cookie,因此儘可能圖片資源放入無cookie服務器,減小與主域名之間的cookie混用

5. 減小TTFB時間

  • cdn
  • 服務器性能
  • ...

6. 減小下載時間

  • 減小response data
  • 利用緩存

7. 升級到http2

http1.1與http2的通道模型

圖片

圖片

http2相比http1.1的優勢

  • 二進制協議

http2採用二進制的傳輸協議,而http1.1採用的是文本傳輸,二進制格式的傳輸效率要比文本快的多。

  • 多路複用

同一個TCP鏈接中能夠同時發送多個請求而不會阻塞

  • 頭部壓縮

對消息頭採用Hpack進行壓縮傳輸,可以節省消息頭佔用的網絡流量,http1.1每次請求,都會攜帶大量冗餘的頭信息,浪費了不少寬帶資源。

  • 服務端push

它容許 Web 服務器在收到瀏覽器的請求以前提早發送一些資源給客戶端

  • http2同時向下兼容http1.x版本

圖片

http2.akamai.com/demo

8. 其餘

8.1 避免空的src,href

// html
<img src="" />
// js
var img = new Image(); 
img.src = "";
複製代碼

雖然src屬性爲空字符串,但瀏覽器仍然會向服務器發起一個HTTP請求:

  • IE 向頁面所在的目錄發送請求;
  • Safari、Chrome、Firefox向頁面自己發送請求;
  • Opera不執行任何操做。

空src產生請求的後果不容小覷:

  • 給服務器形成意外的流量負擔,尤爲時日 PV 較大時;
  • 浪費服務器計算資源;
  • 可能產生報錯。

空的href屬性也存在相似問題。用戶點擊空連接時,瀏覽器也會向服務器發送HTTP請求,能夠經過JavaScript阻止空連接的默認的行爲。

8.2 減小重定向

每一次的重定向都是須要從新再發起一次http請求

8.3 避免404

HTTP請求很昂貴,返回無效的響應(如404未找到)徹底不必,下降用戶體驗並且毫無益處。 一些網站設計很酷炫、有提示信息的404頁面,有助於提升用戶體驗,但仍是浪費服務器資源。尤爲糟糕的是外部腳本返回404,不只阻塞其餘資源下載,瀏覽器還會嘗試把404頁面內容看成JavaScript解析,消耗更多資源。

8.4 IPv4升級到IPv6

由於IPv4即將用完以及主要的移動網絡正在迅速採用IPv6(美國已經達到50% 的 IPv6 使用閾值),將你的 DNS 更新到 IPv6 以應對將來是一個好的想法。只要確保在網絡上提供雙棧支持,就可讓 IPv6 和 IPv4 同時運行。畢竟,IPv6 不是向後兼容的。研究顯示,也是正由於 IPv6 自帶 NDP 以及路由優化,因此纔可以讓網站的載入速度提高10%到15%。

緩存

1. http緩存

當客戶端向服務器請求資源時,會先抵達瀏覽器緩存,若是瀏覽器有「要請求資源」的副本而且沒有失效,就能夠直接從瀏覽器緩存中提取而不是從原始服務器中提取這個資源。

常見的http緩存只能緩存get請求響應的資源,對於其餘類型的響應則無能爲力,因此後續說的請求緩存都是指GET請求。

http緩存都是從第二次請求開始的。第一次請求資源時,服務器返回資源,並在respone header頭中回傳資源的緩存參數;第二次請求時,瀏覽器判斷這些請求參數,命中強緩存就直接200,不然就把請求參數加到request header頭中傳給服務器,看是否命中協商緩存,命中則返回304,不然服務器會返回新的資源。

1.1 強緩存

強制緩存在緩存數據未失效的狀況下(即Cache-Control的max-age沒有過時或者Expires的緩存時間沒有過時),那麼就會直接使用瀏覽器的緩存數據,不會再向服務器發送任何請求。強制緩存生效時,http狀態碼爲200。這種方式頁面的加載速度是最快的,性能也是很好的,可是在這期間,若是服務器端的資源修改了,頁面上是拿不到的,由於它不會再向服務器發請求了。這種狀況就是咱們在開發種常常遇到的,好比你修改了頁面上的某個樣式,在頁面上刷新了但沒有生效,由於走的是強緩存,因此Ctrl + F5一頓操做以後就行了。 跟強制緩存相關的header頭屬性有(Pragma(http1.0)/Cache-Control(http1.1)/Expires)

圖片

1.2 協商緩存

當第一次請求時服務器返回的響應頭中沒有Cache-Control和Expires或者Cache-Control和Expires過時還或者它的屬性設置爲no-cache時(即不走強緩存),那麼瀏覽器第二次請求時就會與服務器進行協商,與服務器端對比判斷資源是否進行了修改更新。若是服務器端的資源沒有修改,那麼就會返回304狀態碼,告訴瀏覽器可使用緩存中的數據,這樣就減小了服務器的數據傳輸壓力。若是數據有更新就會返回200狀態碼,服務器就會返回更新後的資源而且將緩存信息一塊兒返回。跟協商緩存相關的header頭屬性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since)請求頭和響應頭須要成對出現

圖片

協商緩存的執行流程是這樣的:當瀏覽器第一次向服務器發送請求時,會在響應頭中返回協商緩存的頭屬性:ETag和Last-Modified,其中ETag返回的是一個hash值,Last-Modified返回的是GMT格式的最後修改時間。而後瀏覽器在第二次發送請求的時候,會在請求頭中帶上與ETag對應的If-Not-Match,其值就是響應頭中返回的ETag的值,Last-Modified對應的If-Modified-Since。服務器在接收到這兩個參數後會作比較,若是返回的是304狀態碼,則說明請求的資源沒有修改,瀏覽器能夠直接在緩存中取數據,不然,服務器會直接返回數據。

1.3 總結

圖片

圖片

圖片

2. 瀏覽器緩存

  • Storage
    • LocalStorage
    • SessionStorage
    • IndexedDB
    • Web SQL
    • Cookies
  • Cache
    • Cache Storage - service worker
    • Application Cache - 離線緩存(瀏覽器支持率過低)

渲染層優化

1. 防止阻塞渲染

  • css放在header提早加載
  • js文件放在底部,防止阻塞解析
  • 一些不改變dom和css的js使用defer和async屬性告訴瀏覽器能夠異步加載,不阻塞解析

2. 減小重繪和迴流

實際開發中不可必變產生重繪和迴流,咱們只能作到儘量的減小這種行爲的發生

  • 減小dom操做
  • 優化dom結構,將可能產生迴流的元素,使用脫離文檔流的方式佈局。
  • img標籤設置高度
  • dom操做離線操做(display:none),只觸發一次迴流
  • 使用transform來作變形和位移,不會形成迴流

3. 服務端渲染

next,nuxt,等

首頁骨架屏

4. 提升代碼質量

html:

  • 優化dom的層級結構,太深了會增長dom樹的構建時間,對js查找深層節點也會形成很大的負擔
  • meta標籤裏增長對文檔的編碼定義,便於瀏覽器解析
  • 不要在HTML中縮放圖片

css:

  • 減小css嵌套層級,選擇合適的選擇器
  • 對於首屏的關鍵css可使用style標籤內聯
  • 避免使用@import
  • 避免使用css表達式
  • 避免使用濾鏡
  • 動畫渲染使用3d語法,開啓GPU加速

css選擇器的效率排序:

1.id選擇器(#myid) 2.類選擇器(.myclassname) 3.標籤選擇器(div,h1,p) 4.相鄰選擇器(h1+p) 5.子選擇器(ul < li) 6.後代選擇器(li a) 7.通配符選擇器(*) 8.屬性選擇器(a[rel="external"]) 9.僞類選擇器(a:hover, li:nth-child)

js:

  • 不要頻繁操做dom,能夠放在requestAnimationFrame中執行
  • 減小經過js直接修改元素的樣式,能夠經過修改class名的方式統一修改
  • 須要屢次訪問的dom節點,須要經過變量轉存,以避免重複訪問dom節點形成性能損耗
  • 及時清理不用的定時器
  • 對於高頻觸發的事件增長防抖(debounce)、節流(throttle)
  • 圖片懶加載、預加載、默認圖

速效方案

  1. js/css tree shaking
  2. 合理的代碼分割和合並
  3. 代碼壓縮,提早gzip或brotli減小服務器動態壓縮的時間
  4. 選擇合適格式的圖片並壓縮
  5. http協議升級至http2
  6. 靜態資源使用cdn

本文參考

Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

體積減小80%!釋放webpack tree-shaking的真正潛力

爲何要用自適應的圖片

HTTP請求合併 vs HTTP並行請求

X-DNS-Prefetch-Control

前端性能優化之雅虎35條軍規

相關文章
相關標籤/搜索