1. 程式人生 > >【翻譯】10倍效能提升:優化靜態站點——by JonLuca De Caro

【翻譯】10倍效能提升:優化靜態站點——by JonLuca De Caro

10倍效能提升:優化靜態站點——by JonLuca De Caro

原文地址:10x Performance Increases: Optimizing a Static Site by JonLuca De Caro https://link.medium.com/KtPI4fhWdR

幾個月前,我在美國境外旅行,並且我希望向我的朋友展示我的個人靜態網站,我嘗試導航到我的網站,但我花費了比我預期更長的時間。網站中絕對沒有任何的動態內容,只是有一些動畫和響應式設計,但是內容是靜態保持不變的。我對這個結果表示非常震驚,DOM節點內容載入花費了4s,整個網頁載入花費了6.8s。網站中有20個靜態站點請求,傳輸總資料為1Mb,我已經習慣了連線到我在舊金山的伺服器的洛杉磯的1Gb/s 低延遲物聯網速度,這讓這個怪物像閃電般快速。在義大利,以8Mb/秒的網速,這完全是一個不同的畫面。

這是我第一次研究網路優化,到目前為止之前我無論什麼時候想要新增一個庫或者資源,我都會將其拋入並指向到src=‘’。我沒有注意任何形式的效能,從快取到內聯到延遲載入。

我開始尋找有相似經驗的人,但是很不幸,很多關於靜態檔案優化的文獻都已經過時了,都是一些2010年或者2011年的建議或者討論。文庫或者假設都不再是真實的,只是重複以前的理論。

但是我還是找到了兩個很好的資訊資源——高效能瀏覽器網路和Dan Luu 在優化靜態站點方面的類似經驗。雖然在程式碼格式和內容方面我達不到Dan的水平,但是我確實設法讓自己的頁面載入速度大約快了10倍,DOM節點內容載入大約為5分之一秒,整頁面載入只有388ms。(實際是有點不準確,因為它解決了延遲載入)。

過程

這個過程的第一步是分析網站輪廓。我想要弄清楚什麼是載入時間最長的,以及如何最好的並行優化一切。我運行了各種工具來分析我的網站,並且在世界各地進行測試,包括: • https://tools.pingdom.com/ • www.webpagetest.org/ • https://tools.keycdn.com/speed • https://developers.google.com/web/tools/lighthouse/ • https://developers.google.com/speed/pagespeed/insights/ • https://webspeedtest.cloudinary.com/

其中一些提供了有關改進的建議,但是當你的網站有50個請求時,你才可以做到這一點。這從90年代遺留下來的gif空影象,是未使用的資源。(我載入了6種字型卻只使用了其中一種)。

我的網站的時間線 - 我在Web Archive網路存檔上對此進行了測試,因為我沒有擷取原始網站,但它看起來與我幾個月前看到的相似。

我想改進我控制的一切 ———從內容和和通過javascript連線到Web伺服器(Nginx)的速度和DNS設定。

優化

縮小和合並資源

第一件我需要注意的事情是我為CSS和JS(沒有任何形式的HTTP keepalive)製作了十幾個請求,對於不同站點,其中一些是https請求。這增加了對各種CDN或伺服器的多次往返請求,並且一些JS檔案正在請求其他的資源,所以這導致了上面看到的串聯阻塞現象。

我使用webpack將我的所有資源合併到一個js檔案。每當我對我的內容進行更改時,它會自動更新壓縮,將所有依賴項轉換為單個檔案。

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const ZopfliPlugin = require("zopfli-webpack-plugin");

module.exports = {
  entry: './js/app.js',
  mode: 'production',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.css$/,
      loaders: ['style-loader', 'css-loader']
    }, {
      test: /(fonts|images)/,
      loaders: ['url-loader']
    }]
  },
  plugins: [new UglifyJsPlugin({
    test: /\.js($|\?)/i
  }), new ZopfliPlugin({
    asset: "[path].gz[query]",
    algorithm: "zopfli",
    test: /\.(js|html)$/,
    threshold: 10240,
    minRatio: 0.8
  })]

};

我試了不同的方案,以前這個單一的bundle檔案位於我網站的head部分,會形成阻塞。它的最終大小為829kb,包括每一個非圖片資源(css,字型,所有庫和依賴項以及js)。這裡面絕大多數是字型檔案,佔用了829kb中的724kb。

我瀏覽了Font Awesome 庫,並刪除了除了我使用的三個圖示以外的所有,只留下了fa-github,fa-envelope和fa-code圖示。 我使用了一個名為fontello的服務來拉取我需要的圖示,這時候字型資源只需要94kb大小。

網站目前的構建方式,如果我們只要樣式表,那看起來是不正確的,所以我接受了單個bundle.js的阻塞性質。載入時間為118毫秒,比上述要好一個等級。

這也有一些額外的好處,我不再需要指向第三方資源或者CDN,因此使用者不需要 1.對資源執行CDNS查詢; 2.執行https握手; 3.等待資源從第三方完全下載;

雖然CDN和分散式快取可能對大規模的分散式站點有意義,但對我的小型靜態網站沒有意義。額外的100毫秒左右是值得的權衡。

壓縮資源

我載入了8Mb大小的頭部頭像,然後以10% 的寬度高度顯示它。這不僅僅是缺乏優化,這幾乎是對使用者頻寬的浪費使用。

我在這個網站壓縮了所有影象,https://webspeedtest.cloudinary.com/。它還建議我切換到webp格式,但我希望保持更多可能性的瀏覽器相容,所以依然堅持使用jpg格式。其實可以建立一個程式,讓webp僅被傳遞到支援它的瀏覽器。 但是我希望儘可能保持簡單,而且對於增加抽象層程式碼的優勢並不明顯和值得。

改進Web伺服器-Http2,TLS等

我做的第一件事情是轉到https,以前我在埠80上執行Nginx,只是從var/www/html載入檔案。

server{
    listen 80;
    server_name jonlu.ca www.jonlu.ca;

    root /var/www/html;
    index index.html index.htm;
    location ~ /.git/ {
          deny all;
    }
    location ~ / {
        allow all;
    }
}

我首先設定了https並將所有http請求重定向到https,我從Let's Encrypt獲得了我的TLS證書(一個偉大的網站,可以註冊簽署通配證書。)  

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name jonlu.ca www.jonlu.ca;

    root /var/www/html;
    index index.html index.htm;

    location ~ /.git {
        deny all;
    }

    location / {
        allow all;
    }

    ssl_certificate /etc/letsencrypt/live/jonlu.ca/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/jonlu.ca/privkey.pem; # managed by Certbot
}

只需新增http2指令,Nginx就可以充分利用最新HTTP功能的所有優勢特性。 請注意,如果要利用HTTP2(以前的SPDY),則必須使用HTTPS。 在這裡閱讀(https://hpbn.co/http2/)更多相關資訊。

您還可以利用帶有http2_push 命令作用於images / Headshot.jpg;

注意:啟用gzip和TLS可能會使您面臨BREACH風險。 由於這是一個靜態站點,面臨BREACH的實際風險很低,所以保持壓縮是是沒有問題的。

利用快取和壓縮指令

通過Nginx還可以實現更多目標嗎?跳出來的第一件事就是快取和壓縮指令。

我傳送原始的未壓縮的html,只需要一個_gzip和_line命令,就能夠從160000位元組變為8000位元組,減少50%。

我們實際上可以能夠進一步提高這個數字——如果設定Nginx的_gzip_static,它會預先查詢所有請求檔案的預壓縮版本。這與我們上面的webpack config結合使用,我們可以用ZopicPlugin在構建時預壓縮我們所有檔案。這節省了計算資源,並允許我們最大限度的提高壓縮率,而無需權衡速度。

此外,我的網站很少更改,所以我希望更可能長時間的快取資源。這將使得在後期使用者訪問中,不需要重新下載資源(尤其是bundle.js)

我更新的伺服器配置如下所示:請注意,我並沒有講解我所有的更改,例如TCP設定更改,gzip指令和檔案快取。如果您想要了解更多相關資訊,請閱讀有關調優Nginx的文章

worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 30000;

events {
    worker_connections 65535;
    multi_accept on;
    use epoll;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Turn of server tokens specifying nginx version
    server_tokens off;

    open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    add_header Referrer-Policy "no-referrer";

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /location/to/dhparam.pem;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';

    ssl_certificate /location/to/fullchain.pem;
    ssl_certificate_key /location/to/privkey.pem;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
    gzip_min_length 256;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

 和相應的伺服器塊。

server {
    listen 443 ssl http2;

    server_name jonlu.ca www.jonlu.ca;

    root /var/www/html;
    index index.html index.htm;

    location ~ /.git/ {
        deny all;
    }

    location ~* /(images|js|css|fonts|assets|dist) {
        gzip_static on; # Tells nginx to look for compressed versions of all requested files first
        expires 15d; # 15 day expiration for all static assets
    }

}

延遲載入

最後,我對我的實際網站進行了一些小的改動,這也對速度有少許不可忽視的提高。有5個影象是在您按下相應的標籤之前看不到,但是這些圖片還是與其他所有標籤同時載入。(因為他們位於<img src ="">標籤中。

我寫了一個簡短的指令碼,用lazyload類修改這些元素的屬性。只有在單擊相應的框時才會載入這些影象。

$(document).ready(function() {
    $("#about").click(function() {
        $('#about > .lazyload').each(function() {
            // set the img src from data-src
            $(this).attr('src', $(this).attr('data-src'));
        });
    });

    $("#articles").click(function() {
        $('#articles > .lazyload').each(function() {
            // set the img src from data-src
            $(this).attr('src', $(this).attr('data-src'));
        });
    });

});

因此一旦文件完成載入,它將修改<img>標籤,以便它們從<img data-src="">轉到<img src="">,並將其載入到背景。

未來的改進

還有一些其他更改可以提高頁面載入速度-最重要的是,使用Service Workers快取和攔截所有請求,讓站點甚至離線執行,並在CDN上快取內容,以便使用者不需要完整在SF中往返伺服器。 這些都是值得的改變,但對於作為線上簡歷/關於我的頁面的個人靜態網站而言並不是特別重要。

結論

這使得我的頁面載入速度從第一頁載入時的8秒以上變為350ms,後續載入時間為200毫秒。我真的建議您閱讀所有高效能瀏覽器網路High Performance Browser Networking ,它很容易閱讀完,但是卻提供了一個令人難以置信的現代網際網路概述,並對優化現代網際網路模型的每一層提出了很好的建議。

我錯過遺漏了什麼嗎?是否有一些違反最佳實踐或可以進一步改善效能的方法?隨意聯絡JonLuca De Caro!