《從零構建前後分離的web專案》:前端1.0 終 - 前端效能優化 (多圖預警)

莊文達發表於2018-11-27

4000字長文,多圖預警!!!流量慎入!!

效能優化 - 屌絲前端效能優化、上線一條龍

大家好我又來了,本章給大家帶來的內容是:上線和上線後的效能優化

專案地址

本章你會了解

  • 前端需要了解的 docker 基礎知識
  • 部署前端專案到本地/外網服務
  • 前端專案的 gZip 優化
  • 瞭解 CDN 的重要性
  • webpack 按需載入
  • 圖片的相關優化
  • 如何分析專案依賴,方便針對性處理
  • 如何減小 webpack 打包大小/速度

上線

我們通常在本地開發,本地環境和線上也並非完全一樣,很多專案第一次上線幾乎都會遇到本地開發無法復現的問題,可能是字型、樣式的問題,也可能是webpack 編譯的問題、甚至可能是本地的奇葩環境。所以 本地完美執行 ≠ 線上完美執行,我們需要 build 專案,模擬線上測試一下,看看是否可以完美執行,有問題可以方便及時作出調整。

準備

為了避免本教程汙染大家本地環境,推薦大家安裝一個docker,後期運維也會根據 docker 展開。

看到這個 Title :《準備docker》,沒接觸過的前端不要慫,裝一個,勇於跨出第一步,不學習就是等死「點選這裡瞭解 docker

雖然 tomcat nginx apache jboss jetty等等等等都可以作為 http 服務,本章以最常見的 nginx 展開講述:

大白話介紹下 docker

docker 就是用更優雅的方法,做到了虛擬機器的事情,並且做的更好,可程式設計管理叢集。docker 啟動容器,在容器內部執行你的環境,預設各個容器是互相隔離的,當然你可以通過 link network 關聯容器,或者直接使用 docker-compose 編排,啟動容器的前提是映象,也類似與虛擬機器的映象,想跑容器,先得下載「pull」映象。

使用 docker

也許很多人沒用過,沒用過也不講怎麼安裝了,自己去看官網咖中文官網社群版下載中國映象加速,windows 的話可能要開啟虛擬化,linux 推薦 ubuntu, 有篇文章這樣講:為了效能請不要在 centos中執行 Docker檢視翻譯點這裡,幾年前的文章了,現在怎麼樣有待考究。

看下 images 狀態

docker images
複製程式碼

螢幕快照 2018-11-27 16.28.03.png

可以看到我已經有一些映象了「我已經刪除了nginx」

dockerHub 拉 Nginx 映象

docker pull registry.docker-cn.com/library/nginx:latest
複製程式碼

正常 docker pull nginx 即可,中間那段是中國映象源

2018-11-27 16.28.54.gif

螢幕快照 2018-11-27 16.33.04.png

ok,我們成功 pull 下來了 Nginx 的映象。預設儲存的映象名為: registry.docker-cn.com/library/ngi…

打包

進入我們上一章原始碼的目錄,build 一下進行釋出。 上一章原始碼在這裡

npm run build
複製程式碼

2018-11-27 16.43.35.gif

啟動 docker 容器

docker run --name nginx -d -p 8888:80 -v /new-bee/dist:/usr/share/nginx/html  registry.docker-cn.com/library/nginx
複製程式碼

2018-11-27 16.55.18.gif

  • 上調命令一些解釋「不多講,避免消化不良,自己探究」
CMD 解釋
-d 守護程式執行
-p 埠對映   8888 :80 docker80埠對映到本機「宿主機」
-v 掛載宿主機的一個目錄  本機「宿主機」: docker容器
—name 為容器命名

測試一下

http://localhost:8888/#/
複製程式碼

2018-11-27 17.06.00.gif

當然初次嘗試 docker 你可能會有更多的疑問:

  • 你怎麼知道需要將主目錄掛載到: /usr/share/nginx/html ?
  • 能否/怎樣 檢視 Nginx 日誌 ?
  • 容器內的 nginx 能否自定義配置 ?
  • ......

這些小白問題本章簡單講講,後面做自動運維的時候單獨展開講,可以關注我的部落格

gZip

我們可以通過 webpack 壓縮指令碼檔案,上傳到 http 伺服器,瀏覽器瀏覽的時候,經過壓縮的HTTP應答報文是由瀏覽器解壓的,比起壓縮,解壓的速度是非常快的(只要資料正常,可以解壓的話),所以不用擔心瀏覽器用於解壓的時間會降低使用者體驗。事實上,瀏覽器解壓消耗的這點時間比起資料包因為網路擁堵而耽誤的時間要少的多也可控的多。

在瀏覽器發給伺服器的HTTP請求報文中,使用Accept-Encoding欄位標明自己支援的壓縮格式,即自己可以解壓哪幾種壓縮報文(gzip、zlib庫提供的deflate)。伺服器回覆客戶端的HTTP應答報文中,使用Content-Encoding欄位標明該應答報文使用哪種壓縮方式。

gZip 攻破 webpack、nginx

像我這樣屌絲的伺服器一般都買 1M 的,大的資原始檔 hold 不住,一個動輒 400K 的 vendar 檔案這很蛋疼,不上 gZIp 很難受。

開啟 network 觀察一下:

螢幕快照 2018-11-27 17.31.07.png

它有 144K 這麼大

螢幕快照 2018-11-27 17.54.58.png

我們就以 webpack 打包的核心 vendor 為例,我們發現,客戶端向服務端請求了 gZIp 資源 Accept-Encoding: gzip, deflate,但可惜服務端並沒有給我們理想中的 response - Content-Encoding: gzip 的響應, 我們需要排查一下原因。

  • 首先看看 webpack 到底打沒打出來打出來 gZip 呢?看看他的目錄有沒有 js 的 .gz 檔案。

螢幕快照 2018-11-27 17.49.21.png

很遺憾沒有,只有一些壓縮檔案和用於定位的 map 檔案,看來首先我們的打包就出現了問題。

大家還記得當初構建專案我發的這張圖嗎?

螢幕快照 2018-11-27 17.36.24.png

  • package.json 專案描述檔案

開啟看看 build 命令執行了哪個指令碼?

螢幕快照 2018-11-27 17.42.22.png

開啟 build.js 看看執行了哪些內容,難道是 vue-cli 沒有為我們配置好webpack gZip 相關的配置嗎?

螢幕快照 2018-11-27 17.43.47.png

我們發現沒什麼特別的,發現一個 const webpackConfig = require('./webpack.prod.conf') 的依賴,大概就是字面意思(webpack生產配置)進去看看。

螢幕快照 2018-11-27 17.46.37.png

哦,我們看到了,webpack 確實為我們配置了 gZip 相關配置。

可是發現這個配置被這個判斷包裹住了:

if (config.build.productionGzip) {

}
複製程式碼

追蹤下去

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
複製程式碼

我們的全部疑惑都被揭開了,開發者通過註釋這樣告訴我們他的理由,我簡單翻譯一下:

螢幕快照 2018-11-27 17.59.02.png

首先下載一下依賴:

 vim package.json
 
 "devDependencies": {
    "compression-webpack-plugin": "^1.1.12",
    }
複製程式碼

然後 productionGzip 改成 true

  • 廢話不多說打個包試試:
npm run build
複製程式碼

螢幕快照 2018-11-27 18.14.11.png

成功了,出現了 .zg 檔案壓縮包,但是 gZip 是需要服務端的支援的,伺服器通過客戶端請求的 Accept-Encoding 首部開判斷返回哪種格式的指令碼檔案,然後由瀏覽器解壓,我們拉下來的 nginx 映象,nginx 是不會為我們預設配置 gZIp 服務端壓縮的,我們去檢視一下吧。

進入 docker 主機

 docker exec -it nginx /bin/bash
複製程式碼

或者

 docker exec -it nginx "bash"
複製程式碼

2018-11-27 18.30.57.gif

CMD 解釋
exec 進入docker容器
-i -i: 以互動模式執行容器,通常與 -t 同時使用;
-t -t: 為容器重新分配一個偽輸入終端,通常與 -i 同時使用;
-it -it = -i -t
“bash” 或 /bin/bash /bin/bash的作用是因為docker後臺必須執行一個程式,否則容器就會退出

進入 nginx 主機的第一件事

nginx 在哪???

Linux whereis 命令用於查詢檔案。

該指令會在特定目錄中查詢符合條件的檔案。這些檔案應屬於原始程式碼、二進位制檔案,或是幫助檔案。

該指令只能用於查詢二進位制檔案、原始碼檔案和man手冊頁,一般檔案的定位需使用 locate 命令。

語法

whereis [-bfmsu][-B <目錄>...][-M <目錄>...][-S <目錄>...][檔案...]
複製程式碼
  • 檢視 nginx 位置
root@e0017cab245f:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
複製程式碼

螢幕快照 2018-11-27 18.40.42.png

  • 我們在 /usr/share/nginx 找到了根目錄 html/

螢幕快照 2018-11-27 18.41.52.png

  • 我們在 /etc/nginx/ 找到了 nginx 配置檔案

ps 中介軟體這麼多,誰記得住呢,記不住自己看看就行了不是嗎?

  • 檢視一下 nginx 的配置

2018-11-27 18.45.36.gif

確實 gZip 真的沒開啟,被注掉了。

我們開啟 gZip 的註釋,並且防止服務端對 css 偷懶,我們一步到位加上幾行經典配置。

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_comp_level 6;
gzip_types application/javascript text/plain application/x-javascript text/css application/xml text/javascript application/json;
gzip_vary on;
    
複製程式碼

nginx配置 程式碼在這裡

如何修改 docker 內部的配置

就目前來看,你有兩種方法可以選擇:

  • 直接 exec 容器進行修改,增加上述 gZip 程式碼段。缺點:若想重新基於映象構建容器容器內的配置會丟失。(除非你 commit 映象)
  • 獨立出配置檔案,一勞永逸。

我們基於第二點在 new-bee/ 目錄 同級 建立了一個目錄 nginx/ 建立一個同名的 nginx.conf 檔案。

nginx配置 程式碼在這裡

  • 先停止 nginx 容器
docker stop nginx
複製程式碼
  • 刪除 nginx 容器
docker rm nginx
複製程式碼
  • 重新構建 nginx 容器
docker run --name nginx -d -p 8888:80 -v /new-bee/dist:/usr/share/nginx/html -v /nginx/nginx.conf:/etc/nginx/nginx.conf:ro registry.docker-cn.com/library/nginx
複製程式碼
  • 看看效果
http://localhost:8888/#/
複製程式碼

為了避免瀏覽器載入剛才的 304 快取,清除下瀏覽器快取或進行隱身模式

螢幕快照 2018-11-27 22.32.22.png

已經奏效了。

  • 看看大小壓縮到多少

螢幕快照 2018-11-27 22.47.03.png

只有 50K 左右,壓縮了 2/3 的大小,這對於大型專案來說,節省的不只是 100K ,甚至是更多,webpack 或者說 gz 等壓縮演算法,會將所有的大量重複的片段單獨標記,所以重複的越多,壓縮的越多,這對於現在頻寬比金子貴的雲服務來說是十分重要的。

CDN

螢幕快照 2018-11-27 22.50.16.png

大家注意到,有些能用 CDN 的我選擇使用了 CDN,那麼 CDN 對於線上服務來說到底有多重要呢?

原理

請求速度

廢話先不說給大家上個對比圖 測試地址

  • 這是我的 論壇

螢幕快照 2018-11-27 23.40.31.png

可以看到僅有幾個地方還算不錯,其餘地方都是一塌糊塗

  • 這是淘寶

螢幕快照 2018-11-27 23.41.54.png

不用說了吧?不過還好,這部分我們資金不足敗了也很正常,但大家可能也大概知道 CDN 的意義了,主要意義不是節省開源專案伺服器頻寬,而是全國各個節點的訪問速度問題,也就解釋了:我部署的專案訪問速度還不錯,你這裡怎麼這麼慢,你網不好吧?CDN 來告訴你答案。

cookie

我們還是拿實戰的 bbs論壇 舉例子吧,檢視網路狀態:

33249096-90F2-4E73-A6E8-475DA49D38E7.png

使用 CDN 的幾點優勢

  • 訪問快
  • 服務端壓力小
  • webpack打包小且快(下面講)

客戶端的 cookie 是繫結服務端 域名 的, 看上圖,我們需要 XHR 請求攜帶 cookie 訪問服務端獲取對應許可權,但試想一下:每一個 js、img、甚至是css 都攜帶垃圾的 cookie ,在大使用者量下,服務端承受著不應該屬於他的痛苦,這樣的消耗是特別應該避免的,我們可以隨便翻一翻任何一個成熟的網站,都會發現存在自己的 CDN 服務,這樣既優化了中國不同地區的訪問速度,同時也大大減小了服務端的開銷。

節省 webpack 打包大小/速度

很長時間前經歷過公司前端 webpack 編譯特別慢的問題,dev 模式下我們可以注掉開發範圍外的 路由,但是 build 釋出的時候似乎沒法解決,使用了 Happypack 多執行緒打包還是不如人意,查閱資料讀到了 這篇文章

螢幕快照 2018-11-27 23.50.06.png

我們可以把能夠 externals 調的排除掉,然後使用 webpack 的 webpack.DllPlugin 生成依賴庫(這點很重要),大大減少便以速度,DllPlugin 本質上的做法和我們手動分離這些第三方庫是一樣的,但是對於包極多的應用來說,自動化明顯加快了生產效率。

如何分析專案依賴

webpack-bundle-analyzer

其實很多人都知道,可能剛入坑的同學不太瞭解,不管是 npm maven 都有自己一套以來分析工具,當然也都來源於第三方,這裡為大家介紹 npm 的以來分析工具: webpack-bundle-analyzer ,他會在瀏覽器生成一個報表,直觀的展示哪裡大,哪裡需要優化,以及預測 gZip 的大小,還是以 實戰專案為例:

按照官方指引的配置,下載依賴,package.json 檔案指定下 build 的指令碼:

"analyz": "NODE_ENV=production npm_config_report=true npm run buildProd",
複製程式碼

執行一下:

npm run analyz
複製程式碼

2018-11-28 01.02.57.gif

效果:

2018-11-28 01.06.27.gif

分析:

發現了問題,static/ 靜態檔案下 hightlight 檔案比較大,有錢可以考慮下 CDN,node_modules/ 下 element-ui 餓了麼元件比較大,(我比較懶,全域性匯入的,可以用哪個引入哪個避免全域性打包問題)可以優化,然後無聊的同學沒事兒點點玩玩吧。

webpack 按需載入 : 一切皆模組

當打包構建應用時,Javascript 包會變得非常大,影響頁面載入。如果我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被訪問的時候才載入對應元件,這樣就更加高效了。 Webpack 的程式碼分割功能, 實現路由元件的懶載入.

官方詳細說明

官方說的挺詳細了,這裡就偷個懶不上程式碼了,給大家提供一種經典處理方式,我們不放在元件上,直接對路由進行拆分,具體可以看 實戰專案路由 的路由拆分

會發現很多這種註釋:

const Blog = () => import(/* webpackChunkName: "blog" */ '@/container/blog/Blog')
複製程式碼

那麼類似:

/* webpackChunkName: "blog" */
複製程式碼

不是白寫的,他是配合 webpack 對專案各路由拆分的,我們可以看看 實際專案載入情況

2018-11-28 00.41.32.gif

這個 blog.hash.js

不是我們寫的,是 webpack 進行分割的,這樣類似 vue 這樣的單頁面架構,不會載入某模組總是載入全部指令碼,大大提升載入速度。

圖片處理

本來不想講的,簡單說說吧,常用的也就那幾種 svg 、base64、 或使用fastdfs元件類似 CDN 的服務。

base64

簡單來講 base64 會減少你的 http 請求數量,要知道 XHR 可不是省油的燈,他會帶來額外的處理請求和處理響應損耗,以表情為例,動輒幾十個表情 http 請求似乎太智障了一些,通常採用 base64 處理,減少了 http 請求數量,但是增大了圖片本身的體積,如果你用了webpack 且你的表情在本地,那麼 webpack 可以幫你自動進行 base64 編碼哦。

壓縮圖片

使用者上傳的圖片可以通過壓縮圖片大小或質量減少頻寬哦,通常使用 GM 對使用者上傳的有必要大鎖的圖片 壓縮成不同大小的,根據業務載入,比如頭像,預設肯定不會請求原始圖片,今日頭條的正文,使用流量的情況下也會預設載入小圖,這些都不是客戶端能做到的,需要服務端壓縮。

結語

當然這些知識萬里長征的第一步,以後的優化之路茫茫多,能大概想起來的比如 :Lazy-Load(優化首屏體驗)、PWA(構建web APP)、服務端渲染(為了SEO)、骨架屏(提升使用者體驗),後端和服務端文章還沒寫, 1.0 版本就放這些吧,期待第二版填坑,下篇開始後端。

SO - 努力吧!

往期文章

序 - 開源的意義

開篇 - 縱觀WEB歷史演變

探究 - 深入聊聊前後分離架構

準備 - 前端了解過關了嗎?前端基礎架構和技術介紹

實戰 - 5分鐘快速構建規範的前端專案骨架

實戰 - 欲善其事先利其器 繼續打磨前端架構

完善 - 手把手教你快速構建網站佈局

終章 - 前端優化與上線


2018-11-28 01.26.16.gif

ps:mac下求推薦個懶人圖床,七牛開始收費了,mweb 不能直接釋出到七牛了,一張一張上傳,我也很無奈啊。

相關文章