一次網站的效能優化之路 -- 天下武功,唯快不破
首屏作為直面使用者的第一屏,其重要性不言而喻,如何加快載入的速度是非常重要的一課。
本文講解的是:筆者對自己搭建的個人部落格網站的速度優化的經歷。
效果體驗地址:http://biaochenxuying.cn
1. 使用者期待的速度體驗
2018 年 8 月,百度搜索資源平臺釋出的《百度移動搜尋落地頁體驗白皮書 4.0 》中提到:頁面的首屏內容應在 1.5 秒內載入完成。
也許有人有疑惑:為什麼是 1.5 秒內?哪些方式可加快載入速度?以下將為您解答這些疑問!
移動網際網路時代,使用者對於網頁的開啟速度要求越來越高。百度使用者體驗部研究表明,頁面放棄率和頁面的開啟時間關係如下圖所示:
根據百度使用者體驗部的研究結果來看,普通使用者期望且能夠接受的頁面載入時間在 3 秒以內。若頁面的載入時間過慢,使用者就會失去耐心而選擇離開,這對使用者和站長來說都是一大損失。
百度搜索資源平臺有 “閃電演算法” 的支援,為了能夠保障使用者體驗,給予優秀站點更多面向用戶的機會,“閃電演算法”在 2017 年 10 月初上線。
閃電演算法 的具體內容如下:
移動網頁首屏在 2 秒之內完成開啟的,在移動搜尋下將獲得提升頁面評價優待,獲得流量傾斜;同時,在移動搜尋頁面首屏載入非常慢(3 秒及以上)的網頁將會被打壓。
2. 分析問題
未優化之前,首屏時間居然大概要 7 - 10 秒,簡直不要太鬧心。
開始分析問題,先來看下 network :
主要問題:
- 第一個文章列表介面用了 4.42 秒
- 其他的後端介面速度也不快
- 另外 js css 等靜態的檔案也很大,請求的時間也很長
我還用了 Lighthouse 來測試和分析我的網站。
Lighthouse 是一個開源的自動化工具,用於改進網路應用的質量。 你可以將其作為一個 Chrome 擴充套件程式執行,或從命令列執行。 為 Lighthouse 提供一個需要審查的網址,它將針對此頁面執行一連串的測試,然後生成一個有關頁面效能的報告。
未優化之前:
上欄內容分別是頁面效能、PWA(漸進式 Web 應用)、可訪問性(無障礙)、最佳實踐、SEO 五項指標的跑分。
下欄是每一個指標的細化效能評估。
再看下 Lighthouse 對效能問題給出了可行的建議、以及每一項優化操作預期會幫我們節省的時間:
從上面可以看出,主要問題:
- 圖片太大
- 一開始圖片就載入了太多
知道問題所在就已經成功了一半了,接下來便開始優化之路。
2. 優化之路
網頁速度優化的方法實在太多,本文只說本次優化用到的方法。
2.1 前端優化
本專案前端部分是用了 react 和 antd,但是 webpack 用的還是 3.8.X 。
2.1.1 webpack 打包優化
因為 webpack4 對打包做了很多優化,比如 Tree-Shaking ,所以我用最新的 react-create-app 重構了一次專案,把專案升級了一遍,所有的依賴包都是目前最新的穩定版了,webpack 也升級到了 4.28.3 。
用最新 react-create-app 建立的專案,很多配置已經是很好了的,筆者只修改了兩處地方。
- 打包配置修改了 webpack.config.js 的這一行程式碼:
// 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 。
- 還修改了圖片打包大小的限制,這樣子小於 40K 的圖片都會變成 base64 的圖片格式。
{
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]',
},
}
2.1.2 去掉沒用的檔案
比如之前可能覺得會有用的檔案,後面發現用不到了,註釋或者刪除,比如 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)
})
2.1.3 圖片處理
把一些靜態檔案再用 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 時,瀏覽器會再請求這個圖片一次,白白浪費伺服器頻寬。
具體細節請看檔案 文章列表
2.2 後端優化
後端用到的技術是 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 倍,簡直不要太爽, 效果如下:
此刻心情如下:
2.3 伺服器優化
你以為前後端都優化一下,本文就完了 ?小兄弟,你太天真了,重頭戲在後頭 !
筆者伺服器用了 nginx 代理。
做的優化如下:
- 隱藏 nginx 版本號
一般來說,軟體的漏洞都和版本相關,所以我們要隱藏或消除 web 服務對訪問使用者顯示的各種敏感資訊。
如何檢視 nginx 版本號? 直接看 network 的介面或者靜態檔案請求的 Response Headers 即可。
沒有設定之前,可以看到版本號,比如我網站的版本號如下:
Server: nginx/1.6.2
設定之後,直接顯示 nginx 了,沒有了版本號,如下:
Server: nginx
- 開啟 gzip 壓縮
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 即可。
- 設定 expires,設定快取
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 號,是否設定成功看如下幾個欄位就知道了:
- Staus Code 裡面的 form memory cache 看出,檔案是直接從本地瀏覽器本地請求到的,沒有請求伺服器。
- Cache-Control 的 max-age= 604800 看出,過期時間為 7 天。
- Express 是 2019 年 3 月 23 號過期,也是 7 天過期。
注意:上面最上面的用紅色圈中的 Disable cache 是否是打上了勾,打了勾表示:瀏覽器每次的請求都是請求伺服器,無論本地的檔案是否過期。所以要把這個勾去掉才能看到快取的效果。
終極大招:服務端渲染 SSR,也是筆者接下來的方向。
3.1 測試場景
一切優化測試的結果脫離了實際的場景都是在耍流氓,而且不同時間的網速對測試結果的影響也是很大的。
所以筆者的測試場景如下:
- a. 筆者的伺服器是阿里的,配置是入門級的學生套餐配置,如下:
- b. 測試網路為 10 M 光纖寬頻。
3.2 優化結果
優化之後的首屏速度是 2.07 秒。
最後加了快取的結果為 0.388 秒。
再來看下 Lighthouse 的測試結果:
比起優化之前,各項指標都提升了很大的空間。
4. 最後
優化之路漫漫,永無止境,天下武功,唯快不破。
本次優化的前端與後端專案,都已經開源在 github 上了,歡迎圍觀。
前端:https://github.com/biaochenxuying/blog-react
後端:https://github.com/biaochenxuying/blog-node
github 部落格地址:https://github.com/biaochenxuying/blog
如果您覺得這篇文章不錯或者對你有所幫助,請給個贊或者星唄,你的點贊就是我繼續創作的最大動力。
相關推薦
一次網站的效能優化之路 -- 天下武功,唯快不破
首屏作為直面使用者的第一屏,其重要性不言而喻,如何加快載入的速度是非常重要的一課。 本文講解的是:筆者對自己搭建的個人部落格網站的速度優化的經歷。 效果體驗地址:http://biaochenxuying.cn 1. 使用者期待的速度體驗 2018 年 8 月,百度搜索資源平臺釋出的《百度移動搜尋落地頁體
CFT每日一題之 天下武功,唯快不破
首先,進入題目頁面,大意就是說,你必須足夠快(二十年沒白擼的那種) 一開始以為是頁面重定向,然後抓包 欸,在響應包裡發現了什麼 FLAG ,沒錯,就是FLAG,這還不是美滋滋,複製提交 。哎,臥槽,不對,肯定是網路問題,再提交,還不對,思考三秒,哦,知道了,肯定
【Darkarts】天下武功,唯快不破。
專欄達人 授予成功建立個人部落格專欄
【M陽光的專欄】天下武功,唯快不破。
致自己 張無忌用5年練成了《九陽真經》。雖然看似沒有任何收穫,卻硬吃下了滅絕師太的三掌........資料結構與演算法就是程式界的《九陽真經》... 不要被花拳繡腿所迷惑,深厚的內功才是成功的資本!貴在持之以恆..
“雲中論道”之——華山論劍 ,唯快不破:秘笈分享
帶寬 華山 上進 同步 rss 高性能 left 多核 中國 “雲中論道”技術課堂第二課開講時間到~ 這次我們邀請到的是來自微軟開源技術中心的高級產品經理,人稱“驀然汐來”。她將為我們介紹,近一年來微軟開源技術中心在Linux上的進一步努力下,運行於Hyper-V之上的Li
【個人小結】一次資料庫效能優化問題
需求場景:存在表t_result_changelog,表記錄了caseNo的相關資料,有些caseNo已經被刪除,但表中的資料沒有對應清理。 難點分析:判斷表中哪些資料已經被刪除,需要在對應版本的caseInfo_version中查詢 初次嘗試: 選擇
機器學習web服務化實戰:一次吐血的服務化之路
機器學習web服務化實戰:一次吐血的服務化之路 背景 在公司內部,我負責幫助研究院的小夥伴搭建機器學習web服務,研究院的小夥伴提供一個機器學習本地介面,我負責提供一個對外服務的HTTP介面。 說起人工智慧和機器學習,python是最擅長的,其以開發速度快,第三方庫多而
網站效能優化之雪碧圖製作
雪碧圖製作及使用 製作目的:由於網站上有需要小的icon且每次載入的時候都會有許多類似的請求,影響了網站的效能。所以將小圖示合併成一張雪碧圖,從而減少圖片的請求數,優化網站效能。 製作方法: 1、刀耕火種法 利用photoshop把一張張小圖合成一張雪碧圖(工作效率太低不建議使
android 記一次富文字載入之路
文章連結:https://mp.weixin.qq.com/s/69TRkmFL1aNuSqfw4ULMJw 專案中經常涉及到富文字的載入,後臺管理端編輯器生成的一段html 程式碼要渲染到移動端上面,一種方法是前端做成html頁面,放到伺服器上,移動端這邊直接webVie
前端效能優化之路-資料存取小結
接著上一節講的,我們說到過,效能優化的一大痛點就是IO讀寫,這一次我們討論一下,資料讀寫的優化,資料儲存的位置,介質決定了讀取的速度。 主要指的是,應用記憶體(執行時記憶體),遠端記憶體(redis等),本地檔案系統(localstorage),遠端檔案系統(
前端效能優化之路-dom程式設計優化
在前端效能優化上一直有個瓶頸,就是dom,web應用最常見的效能瓶頸就是dom,用指令碼進行dom操作的代價是很昂貴的. 具體體現為幾點: 修改和訪問dom元素 修改dom元素的樣式導致的重繪(repaint)和重排(reflow) 通過dom事件處理與使用者
HelloCpp開發日記:網站效能優化之檔案伺服器分離技術
閒來無事跟朋友折騰了一個山寨百衲本的網站HelloCpp(http://www.hellocpp.net)。介面我們都是外行,反正基本就是活脫脫一個山寨的www.codeproject.com。不過開發過程中還是蠻有一些心得的。現在寫出來跟大家分享。有不對的地方請大家多多建議。 網站效能優化
網站效能優化之_頁面靜態化
現在網際網路發展越來越迅速,對網站的效能要求越來越高,也就是如何應對高併發量。像12306需要應付上億人同時來搶票,淘寶雙十一……所以,如何提高網站的效能,是做網站都需要考慮的。首先網站效能優化的方面有很多:1,使用快取,最傳統的一級二級快取;2,將服務和資料
網站效能優化之GZIP壓縮功能優缺點以及配置方法
開啟GZIP壓縮主要解決靜態資源過多過大,導致客戶端載入資源時間較長的問題,但同時會增加伺服器計算量。所以當頻寬壓力過高而伺服器效能較好時可以使用該方法提高使用者體驗。 1、TOMCAT配置如下
webpack打包效能優化之路
效能優化的路沒有窮盡,只有更快。開啟頁面越快越好,點選響應越快越好。在當今這個以快為主的時代,快才是王道。閒話扯完,說正事!!! 該優化方案以最近做的一個hybrid webapp為例項演示。 路由懶載入 (1)vue-router檔案中的router使用懶載入方式。如下圖所示 (2
記一次wordpress效能優化
wordpress真的很流行,但是我真的不認為它的效能好,尤其當資料超過幾萬十幾萬的時候。當然作為一個個人部落格來說超過幾萬的資料是一件很難的事情。可我現在用wordpress作為CMS使用,資料庫中有十幾萬條資料。 新租的雲主機配置為:2核2GHZ的CPU、1G記憶體、
Android效能優化之路(五年之癢)
記得在上學的時候,就聽說在某個方面堅持學習一年,也就入門了。很慶幸,工作五年了,一直都在做Android效能優化。一般來說,工作三年左右會是工作的一個門檻,五年左右會是另外一個門檻,所以最近非常的低落、迷茫。昨晚外面下著漂泊大雨,輾轉反側的我,在想自己這從業五年
前端效能優化之路——圖片篇。
本人是一名前端開發者,在公司負責目前負責資訊流服務,為五大手機廠商和各大App提供服務,每天的請求就是以億計算,加上我們又做了SSP和DSP,就是類似於百度廣告聯盟,騰訊廣點通這種。接觸過的應該知道,所以前端優化一直是我頭痛的問題,不僅要注重使用者體驗,同時要兼
【運維實戰】一次linux日誌分割之路——將日誌按照每小時進行分割,並按照“日期-小時”格式保存
linu 一次 圖片 威脅 rontab acc 記錄 進一步 bash 是這樣的,現在需要對nginx的access.log進行按照每小時進行分割,並且最好還要能夠以 “日期+時間”的形式,命名保存。 兩點,一個是按照每小時進行分割,一個是將日誌以“日期+時間”的形式進行
記一次介面效能優化實踐總結:優化介面效能的八個建議
### 前言 最近對外介面偶現504超時問題,原因是程式碼執行時間過長,超過nginx配置的15秒,然後真槍實彈搞了一次介面效能優化。在這裡結合優化過程,總結了介面優化的八個要點,希望對大家有幫助呀~ - 資料量比較大,批量操作資料入庫 - 耗時操作考慮非同步處理 - 恰當使用快取 - 優化程式邏輯、程式碼