首屏做爲直面用戶的第一屏,其重要性不言而喻,如何加快加載的速度是很是重要的一課。javascript
本文講解的是:筆者對本身搭建的我的博客網站的速度優化的經歷。css
效果體驗地址: http://biaochenxuying.cnhtml
2018 年 8 月,百度搜索資源平臺發佈的《百度移動搜索落地頁體驗白皮書 4.0 》中提到:頁面的首屏內容應在 1.5 秒內加載完成。前端
也許有人有疑惑:爲何是 1.5 秒內?哪些方式可加快加載速度?如下將爲您解答這些疑問!java
移動互聯網時代,用戶對於網頁的打開速度要求愈來愈高。百度用戶體驗部研究代表,頁面放棄率和頁面的打開時間關係以下圖所示:node
根據百度用戶體驗部的研究結果來看,普通用戶指望且可以接受的頁面加載時間在 3 秒之內。若頁面的加載時間過慢,用戶就會失去耐心而選擇離開,這對用戶和站長來講都是一大損失。react
百度搜索資源平臺有 「閃電算法」 的支持,爲了可以保障用戶體驗,給予優秀站點更多面向用戶的機會,「閃電算法」在 2017 年 10 月初上線。webpack
閃電算法 的具體內容以下:nginx
移動網頁首屏在 2 秒以內完成打開的,在移動搜索下將得到提高頁面評價優待,得到流量傾斜;同時,在移動搜索頁面首屏加載很是慢(3 秒及以上)的網頁將會被打壓。git
未優化以前,首屏時間竟然大概要 7 - 10 秒,簡直不要太鬧心。
開始分析問題,先來看下 network :
主要問題:
我還用了 Lighthouse 來測試和分析個人網站。
Lighthouse 是一個開源的自動化工具,用於改進網絡應用的質量。 你能夠將其做爲一個 Chrome 擴展程序運行,或從命令行運行。 爲 Lighthouse 提供一個須要審查的網址,它將針對此頁面運行一連串的測試,而後生成一個有關頁面性能的報告。
未優化以前:
上欄內容分別是頁面性能、PWA(漸進式 Web 應用)、可訪問性(無障礙)、最佳實踐、SEO 五項指標的跑分。
下欄是每個指標的細化性能評估。
再看下 Lighthouse 對性能問題給出了可行的建議、以及每一項優化操做預期會幫咱們節省的時間:
從上面能夠看出,主要問題:
知道問題所在就已經成功了一半了,接下來便開始優化之路。
網頁速度優化的方法實在太多,本文只說本次優化用到的方法。
本項目前端部分是用了 react 和 antd,可是 webpack 用的仍是 3.8.X 。
由於 webpack4 對打包作了不少優化,好比 Tree-Shaking ,因此我用最新的 react-create-app 重構了一次項目,把項目升級了一遍,全部的依賴包都是目前最新的穩定版了,webpack 也升級到了 4.28.3 。
用最新 react-create-app 建立的項目,不少配置已是很好了的,筆者只修改了兩處地方。
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// 把上面的代碼修改成:
const shouldUseSourceMap = process.env.NODE_ENV === 'production' ? false : true;
複製代碼
生產環境下,打包去掉 SourceMap,靜態文件就很小了,從 13M 變成了 3M 。
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/,/\.jpg$/,/\.svg$/],
loader: require.resolve('url-loader'),
options: {
limit: 40000, // 把默認的 10000 修改成 40000
name: 'static/media/[name].[hash:8].[ext]',
},
}
複製代碼
好比以前可能以爲會有用的文件,後面發現用不到了,註釋或者刪除,好比 reducers 裏面的 home 模塊。
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'
// import { home } from './module/home'
import { user } from './module/user'
import { articles } from './module/articles'
const rootReducer = (history) => combineReducers({
// home,
user,
articles,
router: connectRouter(history)
})
複製代碼
把一些靜態文件再用 photoshop 換一種格式或者壓縮了一下, 好比 logo 圖片,本來 111k,壓縮後是 23K。
首頁的文章列表圖片,修改成懶加載的方式加載。
以前由於不想爲了個懶加載功能而引用一個插件,因此想本身實現,看了網上關於圖片懶加載的一些代碼,再結合本項目,實現了一個圖片懶加載功能,加入了 事件的節流(throttle)與防抖(debounce)。
代碼以下:
// fn 是事件回調, delay 是時間間隔的閾值
function throttle(fn, delay) {
// last 爲上一次觸發回調的時間, timer 是定時器
let last = 0,
timer = null;
// 將throttle處理結果看成函數返回
return function() {
// 保留調用時的 this 上下文
let context = this;
// 保留調用時傳入的參數
let args = arguments;
// 記錄本次觸發回調的時間
let now = +new Date();
// 判斷上次觸發的時間和本次觸發的時間差是否小於時間間隔的閾值
if (now - last < delay) {
// 若是時間間隔小於咱們設定的時間間隔閾值,則爲本次觸發操做設立一個新的定時器
clearTimeout(timer);
timer = setTimeout(function() {
last = now;
fn.apply(context, args);
}, delay);
} else {
// 若是時間間隔超出了咱們設定的時間間隔閾值,那就不等了,不管如何要反饋給用戶一次響應
last = now;
fn.apply(context, args);
}
};
}
// 獲取可視區域的高度
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
// 用新的 throttle 包裝 scroll 的回調
const lazyload = throttle(() => {
// 獲取全部的圖片標籤
const imgs = document.querySelectorAll('#list .wrap-img img');
// num 用於統計當前顯示到了哪一張圖片,避免每次都從第一張圖片開始檢查是否露出
let num = 0;
for (let i = num; i < imgs.length; i++) {
// 用可視區域高度減去元素頂部距離可視區域頂部的高度
let distance = viewHeight - imgs[i].getBoundingClientRect().top;
// 若是可視區域高度大於等於元素頂部距離可視區域頂部的高度,說明元素露出
if (distance >= 100) {
// 給元素寫入真實的 src,展現圖片
let hasLaySrc = imgs[i].getAttribute('data-has-lazy-src');
if (hasLaySrc === 'false') {
imgs[i].src = imgs[i].getAttribute('data-src');
imgs[i].setAttribute('data-has-lazy-src', true); //
}
// 前 i 張圖片已經加載完畢,下次從第 i+1 張開始檢查是否露出
num = i + 1;
}
}
}, 1000);
複製代碼
注意:給元素寫入真實的 src 了以後,把 data-has-lazy-src 設置爲 true ,是爲了不回滾的時候再設置真實的 src 時,瀏覽器會再請求這個圖片一次,白白浪費服務器帶寬。
具體細節請看文件 文章列表
後端用到的技術是 node、express 和 mongodb。
後端主要問題是接口速度很慢,特別是文章列表的接口,已是分頁請求數據了,爲何還那麼慢呢 ?
因此查看了接口返回內容以後,發現返回了不少列表不展現的字段內容,特別是文章內容都返回了,而文章內容是很大的,佔用了不少資源與帶寬,從而使接口消耗的時間加長。
從上圖能夠看出文章列表接口只要返回文章的 標題、描述、封面、查看數,評論數、點贊數和時間便可。
因此把不須要給前端展現的字段註釋掉或者刪除。
// 待返回的字段
let fields = {
title: 1,
// author: 1,
// keyword: 1,
// content: 1,
desc: 1,
img_url: 1,
tags: 1,
category: 1,
// state: 1,
// type: 1,
// origin: 1,
// comments: 1,
// like_User_id: 1,
meta: 1,
create_time: 1,
// update_time: 1,
};
複製代碼
一樣對其餘的接口都作了這個處理。
後端作了處理以後,全部的接口速度都加快了,特別是文章列表接口,只用了 0.04 - 0.05 秒左右,相比以前的 4.3 秒,速度提升了 100 倍,簡直不要太爽, 效果以下:
此刻心情以下:
你覺得先後端都優化一下,本文就完了 ?小兄弟,你太天真了,重頭戲在後頭 !
筆者服務器用了 nginx 代理。
作的優化以下:
通常來講,軟件的漏洞都和版本相關,因此咱們要隱藏或消除 web 服務對訪問用戶顯示的各類敏感信息。
如何查看 nginx 版本號? 直接看 network 的接口或者靜態文件請求的 Response Headers 便可。
沒有設置以前,能夠看到版本號,好比我網站的版本號以下:
Server: nginx/1.6.2
複製代碼
設置以後,直接顯示 nginx 了,沒有了版本號,以下:
Server: nginx
複製代碼
nginx 對於處理靜態文件的效率要遠高於 Web 框架,由於可使用 gzip 壓縮協議,減少靜態文件的體積加快靜態文件的加載速度、開啓緩存和超時時間減小請求靜態文件次數。
筆者開啓 gzip 壓縮以後,請求的靜態文件大小大約減小了 2 / 3 呢。
gzip on;
#該指令用於開啓或關閉gzip模塊(on/off)
gzip_buffers 16 8k;
#設置系統獲取幾個單位的緩存用於存儲gzip的壓縮結果數據流。16 8k表明以8k爲單位,安裝原始數據大小以8k爲單位的16倍申請內存
gzip_comp_level 6;
#gzip壓縮比,數值範圍是1-9,1壓縮比最小但處理速度最快,9壓縮比最大但處理速度最慢
gzip_http_version 1.1;
#識別http的協議版本
gzip_min_length 256;
#設置容許壓縮的頁面最小字節數,頁面字節數從header頭得content-length中進行獲取。默認值是0,無論頁面多大都壓縮。這裏我設置了爲256
gzip_proxied any;
#這裏設置不管header頭是怎麼樣,都是無條件啓用壓縮
gzip_vary on;
#在http header中添加Vary: Accept-Encoding ,給代理服務器用的
gzip_types
text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
text/javascript application/javascript application/x-javascript
text/x-json application/json application/x-web-app-manifest+json
text/css text/plain text/x-component
font/opentype font/ttf application/x-font-ttf application/vnd.ms-fontobject
image/x-icon;
#進行壓縮的文件類型,這裏特別添加了對字體的文件類型
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
#禁用IE 6 gzip
複製代碼
把上面的內容加在 nginx 的配置文件 ngixn.conf 裏面的 http 模塊裏面便可。
是否設置成功,看文件請求的 Content-Encoding 是否是 gzip 便可。
server {
listen 80;
server_name localhost;
location / {
root /home/blog/blog-react/build/;
index index.html;
try_files $uri $uri/ @router;
autoindex on;
expires 7d; # 緩存 7 天
}
}
複製代碼
我從新刷新請求的時候是 2019 年 3 月 16 號,是否設置成功看以下幾個字段就知道了:
注意:上面最上面的用紅色圈中的 Disable cache 是不是打上了勾,打了勾表示:瀏覽器每次的請求都是請求服務器,不管本地的文件是否過時。因此要把這個勾去掉才能看到緩存的效果。
終極大招:服務端渲染 SSR,也是筆者接下來的方向。
一切優化測試的結果脫離了實際的場景都是在耍流氓,並且不一樣時間的網速對測試結果的影響也是很大的。
因此筆者的測試場景以下:
優化以後的首屏速度是 2.07 秒。
最後加了緩存的結果爲 0.388 秒。
再來看下 Lighthouse 的測試結果:
比起優化以前,各項指標都提高了很大的空間。
優化之路漫漫,永無止境,天下武功,惟快不破。
本次優化的項目,已經開源在 github 上了, 地址:https://github.com/biaochenxuying/blog-react
若是您以爲這篇文章不錯或者對你有所幫助,請給個贊吧,您的確定就是我繼續創做的最大動力。