前言
事實上, 只有10%-20%
的最終用戶響應時間是發在從Web服務器獲取HTML文檔並傳送到瀏覽器中的。如果希望能夠有效地減少頁面的響應時間,就必須關注剩餘80%-90%
的最終用戶體驗。
--Steve Souders在這篇博客中,我根據工作中的實際項目經驗和一些測試的經驗中總結出了前端頁面在性能上優化方案。其中一些經驗吸收自《高性能網站建設指南》Steve Souders 著 電子工業出版社。
一、 代碼相關優化
1. 將樣式表放在首部-使用link標籤將樣式表放在文檔的HEAD中
-
遵循HTML規範
,將樣式表放在頭部,可以有效避免白屏
和無樣式內容的閃爍
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<!-- 使用link標籤將樣式表放在文檔的HEAD中 -->
<link rel="stylesheet" href="example.css">
</head>
<body></body>
</html>
2. 將腳本放在底部
- 將腳本放在頂部會造成的影響:
腳本阻塞對其後面內容的顯示
;腳本會阻塞對其後面組件的下載
; - 將腳本放在
底部</body>
標籤之前, 類似於document.body.appendChild(yourScript), 不會阻塞頁面內容的呈現,而且頁面中的可視組件可以儘早下載。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<link rel="stylesheet" href="example.css">
</head>
<body>
<!-- 將腳本放在底部 -->
<script src="example.js"></script>
</body>
</html>
3. 減少HTTP請求
1) CSS Sprites (雪碧圖)
將多個圖片
合成一張圖片
,通過background-position來定位所需要的圖片。每次請求的話只需要請求一張圖片
減少http請求。(如果使用圖標的話建議使用svg,也可以使用iconfont)
- 合成雪碧圖的工具有很多
- 本地工具:https://github.com/iwangx/sprite(國人寫的)
- 在線工具https://www.toptal.com/develo...
本地工具:
在線工具:
2) 內聯圖片和腳本
- 通過內聯圖片和腳本無需額外的HTTP請求,圖片小於10K的可以設置內聯爲base64位。
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4SUJRXhpZgAATU>
3) 合併腳本和樣式表
- 一般來說,使用外部腳本和樣式表對性能更有利,然而如果將模塊化的代碼分開放到
多個小文件
中,會降低性能
,每個文件都會導致一個額外的
HTTP請求
4. 使用外部Javascript和css
Good
<link rel="stylesheet" href="example.css">
<script src="example.js"></script>
bad
<style>
// code
</style>
<script>
// code
</script>
- 使用外部Javascript和Css的主要作用有:
可以配置緩存
有利於組件重用
5. 使用CDN (內容分發網絡 Content Delivery Network)
CDN是構建在網絡之上的內容分發網絡,依靠部署在各地的邊緣服務器
,通過中心平臺的負載均衡
、內容分發
、調度
等功能模塊,使用戶就近獲取所需內容,降低網絡擁塞,提高用戶訪問響應速度和命中率。CDN的關鍵技術主要有內容存儲
和分發技術
。--摘自百度百科
- 通過CDN引入的資源目前基本都是使用目前最新的
HTTP2協議
,所以在性能上可以做到極致優化,感謝CDN。 - BootCDN - Bootstrap 中文網開源項目免費 CDN 加速服務
- UNPKG
6. 代碼壓縮
1) Gzip 壓縮
gzip壓縮可以節省50%-70%
的網絡開銷
瀏覽器支持的壓縮類型可以通過network的Accept-Encoding:gzip
,deflate
來查看。支持deflate的瀏覽器也支持gzip,但很多瀏覽器支持gzip卻不支持deflate
,因此gzip是最理想的壓縮方法
- node端 使用compression如果是
webpack
項目可以看下面的Vue首屏加載時間優化
方案裏的gzip壓縮
// npm install compression --save-dev
const compression = require('compression')
2) 代碼壓縮
前端打包壓縮的有grunt,gulp,webpack,可以對HTML,CSS,Javascript代碼壓縮
二、 服務器相關優化
本文中使用nginx
服務器進行相關配置,使用apache
同樣可以做到相關優化,具體操作請另Google
1. 開啓gzip
壓縮
- 服務端配置gzip壓縮
gzip on; # 開啓Gzip
gzip_static on; # 開啓靜態文件壓縮
gzip_min_length 1k; # 不壓縮臨界值,大於1K的才壓縮
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss; # 進行壓縮的文件類型
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
- 我的服務器nginx相關的配置:
2. 開啓HTTP2
HTTP2在前端性能上主要表現在:請求和響應的多路複用、頭部壓縮
- 感受下多路複用
nginx服務器配置HTTP2
- 使用http2需要配合https使用
- 使用https需要ca證書 阿里雲https證書購買 (有免費的ca證書)
3. 開啓緩存
添加Expires頭(強緩存)
個人站點相關配置
nginx配置
location ~.*\.(svg|woff|js|css){
root /yourFilePath;
expires 1d;
}
- Web服務器使用
Expires
頭來告訴Web客戶端它可以使用一個組件的當前副本,直到指定的時間爲止 HTTP規範中簡要地稱該頭爲“在這一日期時間之後,響應將被認爲是無效的”。它在HTTP響應中發送
expires: Thu, 30 May 2019 20:51:42 GMT
- 上面的Expires頭告訴瀏覽器該響應的有效性持續到2019年5月30日爲止。如果爲頁面中的一個圖片返回了這個頭,瀏覽器在後續的頁面瀏覽中會使用緩存的圖片,將HTTP請求的數量減少一個
Max-Age和mod_expires
- 個人站點的css文件使用
強緩存cache-control: max-age
- nginx配置
server {
add_header Cache-Control max-age=72000;
}
- 在解釋緩存如何很好地改善傳輸性能之前,需要提及除了Expires 頭之外的另一種選擇。
HTTP 1.1引入了Cache-Control頭來克服Expires頭的限制
- 因爲Expires頭使用一個特定的時間,它
要求服務器和客戶端的時鐘嚴格同步
。另外,過期日期需要經常檢查,並且一旦未來這天到來了,還需要在服務器配置中提供個新的日期。 - Cache-Control使用max-age指令指定組件被緩存多久, 如果從組件被請求開始過去的秒數少於max-age,瀏覽器就使用緩存的版本,這就避免了額外的HTTP請求。
一個長久的max-age頭可以將刷新窗設置爲未來10年
。
Cache-Control: max-age=315360000
- Expires 和Cache-Control max-age.如果兩者同時出現,HTTP規範規定max-age指令將
重寫Expires頭
。 - 建議使用
Cache-Control max-age
,如果使用expires你需要擔心它帶來的時鐘同步
和配置維護
問題。
配置ETag(協商緩存)
Vue官方文檔的Expires相關配置
瀏覽器必須產生這個HTTP請求,執行有效性檢查, 但這仍比簡單地下載所有已過期的組件效率要高
(對比強緩存
)。如果瀏覽器緩存中的組件是有效的(即它能夠和原始服務器上的組件相匹配),原始服務器不會返回整個內容,而是返回一個304 Not Modifed
狀態碼。
附:Vue首屏加載時間過長詳細優化方案
- 首先附一張優化過後的圖
-
首屏加載時間
從原來的10s
到2s
,測試的個人站點
注:我在優化vue項目的時候使用的是[email protected]
,[email protected]
。 如果是cli2的項目影響也不大,優化的方案是結合服務端和webpack的。
vue-cli腳手架默認配置已經大幅度優化了前端整體的性能,在此基礎上,我又使用了三個優化項增加了大幅度提升
1. gzip壓縮
- 結合
服務器相關優化第一條:開啓gzip壓縮
- 下面是前端項目中
vue.config.js
中的配置
// 需要 npm install compression-webpack-plugin --save-dev
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 定義當前環境
const ENV = process.env.NODE_ENV || 'development'
module.exports = {
configureWebpack: config => {
// 如果是開發環境的話,開啓壓縮
if (ENV === 'production') {
// 參數配置文檔: https://www.webpackjs.com/plugins/compression-webpack-plugin/
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240,
minRatio: 0.8
}))
}
}
}
2. 使用CDN內容分發網絡
在index.html
文件中通過環境來判斷是否引入cdn文件,在vue.config.js
文件中webpack通過環境判斷是否使用cdn引入文件的全局變量
- 使用CDN需要在
webpack
和index.html
進行相關配置
第一步 配置vue.config.js
,只需要在剛纔配置Gzip壓縮的基礎上再加一段代碼
:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const ENV = process.env.NODE_ENV || 'development'
module.exports = {
configureWebpack: config => {
if (ENV === 'production') {
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240,
minRatio: 0.8
}))
// 配置externals就是當使用CDN進入的js文件在當前項目中可以引用
// 比如在開發環境引入的vue是import Vue from 'vue', 這個大寫的Vue就是對應的下面的大寫的Vue
config.externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'axios': 'axios'
}
}
}
}
第二步 配置index.html
,在body裏使用EJS語法判斷是否爲生產環境
<body>
<div id="app"></div>
<% if (NODE_ENV === 'production') { %>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<% } %>
</body>
3. 配置sourceMap
- devtool | webpack中文網 你可以根據官網來對開發環境和生產環境進行詳細配置
- 當然也可以像我一樣直接
productionSourceMap: false
幹掉生產環境的sourceMap
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const ENV = process.env.NODE_ENV || 'development'
module.exports = {
configureWebpack: config => {
if (ENV === 'production') {
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240,
minRatio: 0.8
}))
config.externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'axios': 'axios'
}
}
},
// 禁用生產環境的sourceMap
productionSourceMap: false
}
4. 使用HTTP2
請參考服務端優化第二條
結語
前端性能優化至關重要,文中提及的是我認爲比較重要的幾點,以後遇到更好的方案會補充進來。當然你也可以在評論區留言我們一起探討,有錯誤的地方歡迎指出。