天天看點

前端資源請求速度優化

前端資源請求速度優化

DNS解析

當浏覽器從第三方伺服器請求資源時,必須先将該跨域域名解析為IP位址,然後浏覽器才能送出請求,此過程稱為DNS解析。

DNS作為網際網路的基礎協定,其解析的速度似乎很容易被網站優化人員忽略,現在大多數新流量乃全已經針對DNS解析進行了優化,比如DNS緩存。

典型的一次DNS解析需要耗費20-120毫秒,所花費的時間幾乎可以忽略不計,但是當網站中使用的資源依賴多個不同域的時候,時間就會成倍增加,進而增加了網站的加載時間。

比如某些圖檔較多的頁面中,在發起圖檔加載請求之前預先把域名解析好将會有至少5%的圖檔加載速度提成。

一般來說前端與華中與DNS有關的有兩點,一是減少DNS的請求次數(緩存DNS位址),二是進行DNS的預擷取,DNS Prefetch。

dns緩存可以在伺服器設定DNS緩存的時間,不經常變更的ip建議設定的時間長一些。盡可能使用A或者AAAA代替CNAME,使用CND加速域名。還可以自己搭建DNS服務。

DNS與解析可以在頁面中通過link标簽來實作。

<link rel="dns-prefetch" href="https://fonts.googleapis.com" />      

DNS與解析隻能解析不同域,同域是不能解析的,因為已經解析完了。dns-prefetch要慎用,不要每個頁面都添加,會造成資源浪費。

預設情況下浏覽器會對目前頁面中所有出現的域名進行預解析,及時沒有寫link标簽,這是隐式解析。

HTTP1.1長連結

經過DNS解析擷取到IP之後就要進行TCP的連結進行資料傳輸。

HTTP協定的初始版本中,每進行一次HTTP通信就要斷開一次TCP連結,也就是短連接配接。

以早期的通信情況來說,因為都是些容量很小的文本傳輸,是以即使這樣也沒有多大問題,但是随着HTTP的大量普及,文旦中包含大量富文本的情況多了起來。每次的請求都會造成無謂的TCP連結建立和斷開,增加通信錄的開銷。

為了解決這個問題,有些浏覽器在請求時,用了一個非标準的Connection字段。這個字段要求伺服器不要關閉TCP連結,以便其他請求複用,伺服器同樣回應這個字段。​

Connection: keep-alive      

一個可以複用的TCP連結就建立了,直到用戶端或伺服器主動關閉連結,但是這并非标準字段,不同實作的行為可能不一緻,還可能造成混亂。

長連結​

HTTP1.1版本在1997年1月釋出,最大的變化就是引入了持久連結,即TCP連結預設不關閉,可以被多個請求複用,不需要再聲明Connection: keep-alive。

持久連接配接減少了TCP連結的重複建立和斷開所造成的的額外開銷,減輕了伺服器端的負載。減少開銷的時間讓HTTP請求和響應能夠更早的結束,這樣Web頁面的速度也就響應變快了。

用戶端和伺服器發現對方一段時間沒有活動,就可以主動關閉連結,不過規範的做法是用戶端在最後一個請求時發送Connection: close,明确要求伺服器關閉連結。目前對于同一個域名,大多數浏覽器允許同時建立6個持久連結。

管道機制​

同一個TCP連結裡面用戶端可以同時發送多個請求,這樣就進一步改變了HTTP協定的效率。

從前發送請求後需等待及接收響應,才能發送下一個請求,管道化技術出現後不用等待響應即可直接發送下一個請求,這樣就能夠做到同時并行發送多個請求,而不需要一個接一個的等待響應了。

管道化技術比持久化連結還要快,請求數越多時間差越明顯。

一個TCP連結可以傳送多個回應,勢必就要有一種機制,區分資料包是屬于哪一個回應的,這就是Content-length字段的作用,聲明本次回應的資料長度。​

Content-Length: 3000      

上面代碼告訴浏覽器,本次回應的長度是3000個位元組,後面的位元組就屬于下一個回應了。

在1.0版本中,Content-Length字段不是必須的,因為浏覽器發現伺服器關閉了TCP連結,就表明收到的資料包已經完成了。

分塊傳輸​

使用Content-Length字段的前提條件是,伺服器發送回應之前,必須知道回應的資料長度。

對于一些耗時的動态操作來說,意味着,伺服器要等到所有操作完成,才能發送資料,顯然這樣的效率不高,更好的方法是産生一塊資料就發送一塊,采用流模式取代緩存模式。

是以1,1規定可以不使用content-length字段,而是用分塊傳輸編碼,隻要請求或響應頭資訊有Transfer-Encoding字段,就表明響應将又數量未定的資料塊組成。​

Transfer-Encoding: chunked      

每個非空資料塊之前會有一個16進制的數值,表示這個塊的的長度,最後是一個大小為0的塊,表示本次回應的資料發送完了。

HTTP/1.1 200 OK
...
25
This is the data in the first chunk
...
2
...
4
...
0
...      

雖然HTTP1.1允許複用TCP連結,但是同一個TCP連結裡面,所有的資料通信是按次序進行的,伺服器隻有處理完一個回應才會進行下一個回應。

如果前面的請求慢,後面就會有需要請求排隊,稱為對頭阻塞。

為了避免這種問題,可以減少請求數或者同僚多開持續請求。

這就出現了很多的優化技巧,比如說。合并腳本和樣式表,将圖檔嵌入css代碼,域名分片等等。

其實如果HTTP協定設計的更好一些,這些額外的工作都是可以避免的。

HTTP2協定

為了解決響應阻塞問題2015年推出了HTTP2。

HTTP2主要用于解決HTTP1.1效率不高的問題,他不叫HTTP2.0是因為不打算釋出子版本了,下一個版本直接就叫HTTP3。

二進制協定​

HTTP1.1頭資訊肯定是文本,資料體可以是文本也可以是二進制,HTTP2則是一個徹底的二進制協定,頭資訊和資料體都是二進制,并且統稱為幀,頭資訊幀和資料幀。

二進制協定的一個好處是可以定義額外的幀,HTTP2定了一近十種幀,為将來的進階應用打好基礎,如果使用文本實作這種功能,解析資料将會變得非常麻煩,二進制解析則友善很多。

多工​

HTTP2複用TCP連結,在一個連結裡,用戶端和浏覽器都可以同時發送多個請求或回應,而且不用按照順序一一對應,這樣就避免了堵塞。

在一個TCP連結裡面,伺服器同時收到了A請求和B請求,先回應了A請求結果發現處理過程非常耗時,先發送A請求已經處理好的部分,再回應B請求,完成後再發送A請求剩餘的部分。

這種雙向的,實時通信就叫做多工。

效果位址: https:http2.akamai.com/demo

資料流​

因為HTTP2的資料包是不按順序發送的,同一個連結裡面連續的資料包,可能屬于不同的回應,是以必須要對資料包做标記,指出他屬于哪個回應。

HTTP2将每個請求或回應的所有資料包,稱為一個資料流,每個資料流都有一個獨一無二的編号,資料包發送的時候,都必須标記資料流ID,用來區分它屬于哪個資料流,另外還規定,用戶端發出的資料流,ID一律為奇數,伺服器釋出的,ID為偶數。

資料流發送到一半的時候,用戶端和伺服器都可以發送信号取消這個資料流。

1.1版本取消資料的唯一方法就是關閉TCP連結,HTTP2可以取消某一次請求,同時保證TCP連結還開着,可以被其他請求使用。

用戶端還可以指定資料流的優先級,優先級越高,伺服器就會越早回應。

壓縮頭資訊​

HTTP協定不帶有狀态,每次請求都必須附上所有資訊,是以請求的很多字段都是重複的,比如Cookie和User Agent,一模一樣的内容每次請求都必須附帶,這會浪費很多帶寬也影響速度。

HTTP2對這一點做了優化,引入了頭資訊壓縮機制,一方面頭資訊使用gzip或compress壓縮後再發送。

另一方面,用戶端和伺服器同時維護一張頭資訊表,所有字段都會存入這個表,生成一個索引号,以後就不發送這個字段隻發送索引号這樣就提高速度了。

伺服器推送​

HTTP2允許伺服器未經過請求主動向用戶端發送資源,這就叫伺服器推送。

常見場景是用戶端請求一個網頁,這個網頁包含很多靜态資源,正常情況下,用戶端必須收到網頁後解析html編碼,發現有靜态資源再發出靜态資源請求,其實伺服器可以預期到用戶端請求網頁後很可能會再請求靜态資源,所有就主動把這些靜态資源随着網頁一起發給用戶端了。

這個功能還是建議考慮自身的需要,會增加一部分成本開銷。

壓縮傳輸資料資源​

通過壓縮傳輸資料資源提升性能體驗。預設HTTP進行資料傳輸資料是沒有進行壓縮的,原始資料多大傳輸的資料就多大。

我們都知道檔案壓縮之後資料體積減少是很客觀的。

響應資料壓縮​

HTTP響應資料一般會根據資料的類型進行壓縮方案的處理,比如文本最常用的方案就是Gzip的壓縮方案,目前大部分的網站都采用這種壓縮方式。

gzip​

浏覽器再請求伺服器的時候會在請求頭中通過Accept-Encoding字段辨別可以接收gzip壓縮方案,伺服器在收到請求後可以擷取到這種壓縮方案,将資源壓縮後傳回給浏覽器,并且在響應頭中加入Content-Encoding字段,值為gzip。

如果用戶端不添加Accept-Encoding頭,伺服器傳回了Content-Encoding,用戶端如果支援的話也會正常解析。

Accept-Encoding基本是浏覽器自動添加的。​

const zlib = require('zlib');
const fs = require('fs');
const rs = fs.cerateReadStream('jquery.js');
const ws = fs.cerateWriteStream('jquery.js.gz');
const gz = zlib.createGzip();
rs.pipe(gz).pipe(ws);
ws.on('error', (err) => {
   console.log('失敗');
})
ws.on('finish', () => {
   console.log('完成')
})      

正常工作中gzip一般可以在nginx伺服器中開啟,不需要自己編寫。還是比較簡單的。

gzip一般是針對文本檔案,比如js,css,對于圖檔來說一般是在開發階段壓縮。

請求資料壓縮​

HTTP2以前請求頭是不可以壓縮的,HTTP2引入了頭資訊壓縮機制,一方面頭資訊使用gzip或express壓縮後再發送。

另一方面,用戶端和伺服器同時維護一張頭資訊表,通過索引字段來傳輸,減少廳資訊資料體積。

實際工作中會存在請求正文非常大的場景,比如發表長篇部落格,上報用于調試網絡資料等等,這些資料如果能在本地壓縮後再送出就可以節省網絡流量,減少傳輸時間。

DFLATE是一種使用Lempel-Ziv壓縮算法的哈夫曼編碼壓縮格式。

ZLIB是一種使用DEFLATE的壓縮格式。

GZIP是一種使用DEFLATE的壓縮格式。

Content-Encoding中的deflate實際上是ZLIB。

前端發送的時候可以進行壓縮:​

const rawBody = 'content=test';
const rawLen = rawBody.length;
const bufBody = new Unit8Array(rawLen);
for (let i = 0; i < rawLen; i++) {
   bufBody[i] = rawBody.charCodeAt(i);
}
const format = 'gzip';
let buf;
switch (format) {
   case gzip': buf = window.pako.gzip(bufBody); break;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', '/service/');
xhr.setRequestHeader('Content-Encoding', format);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhr.send(buf);      

伺服器端進行解壓​

const http = require('http');
const zlib = require('zlib');
http.createServer((req, res) => {
   let zlibStream;
   const encoding = req.headers['content-encoding']
   switch (encoding) {
       case 'gzip' : zlibStream = zlib.createGunzip(); break;
   }
   res.writeHead(200, { 'Content-Type': 'text/plain' });
   req.pipe(zlibStream).pipe(res);
}).listen(3000)      

這種壓縮一半也隻适用于文本,如果資料量太大壓縮過程也是比較耗時的。

緩存

緩存的原理是在用戶端首次請求後儲存一份請求資源的響應副本存儲在用戶端中,當使用者再次發起相同的請求後,如果判斷緩存命中則攔截請求,将之前緩存的響應副本傳回給使用者,進而避免重新向伺服器發起資源請求。

緩存的技術種類有很多,比如代理緩存,浏覽器緩存,網關緩存,負載均衡器及内容分發網絡等,大緻可以分為兩類,共享緩存和私有緩存。

共享緩存指的是緩存内容可以被多個使用者使用,如公司内部架設的Web代理,私有緩存是隻能單獨被使用者使用的緩存,如浏覽器緩存。

HTTP緩存是前端開發中最常接觸的緩存機制之一,他又可細分為強制緩存與協商緩存,二者最大的差別在于判斷緩存命中時浏覽器是否需要向伺服器進行詢問。

強制緩存不會去詢問,協商緩存則仍舊需要詢問伺服器。

強制緩存​

對于強制緩存而言,如果浏覽器判斷所請求的目标資源有效命中則可直接從強制緩存中傳回請求的響應,無需與伺服器進行任何通信。

也就是說強制緩存是在用戶端進行的,這樣速度就會很快。

強制緩存相關的兩個字段是expires和cache-control。

expires是在HTTP1.0協定中聲明的用來控制緩存失效日期的時間戳字段,他由伺服器指定并通過響應頭告訴浏覽器,浏覽器在收到帶有該字段的響應體後進行緩存。

之後浏覽器再發送相同的請求便會對比expires與本地目前的時間戳,如果目前請求的本地時間戳小于expires的值,則說明緩存還未過期,可以直接使用。

否則緩存過期重新向伺服器發送請求擷取響應體。

res.writeHEAD(200, {
   Expires: new Date('2021-6-18 12: 51: 00').toUTCString(),
})      

expires存在一個很大的漏洞就是對本地時間戳過分依賴,如果用戶端本地的時間與伺服器時間不同步,或者用戶端時間被修改,那麼緩存過期的判斷可能就無法和預期相符。

為了解決這個問題,HTTP1.1新增了cache-control字段來對expires的功能進行擴充和完善。

cache-control的值為maxage=xxx來控制響應資源的有效期,xxx是一個以秒為機關的時間長度,表示該資源在被請求到的一段時間内有效,以此便可避免服務端和用戶端時間戳不同步而造成的問題。

res.writeHEAD(200, {
   'Cache-Control': 'maxage=1000',
})      

除了maxage還可以設定其他參數,比如no-cache和no-store。

no-cache表示強制進行協商緩存,對于每次發起的請求不會再去判斷強制緩存是否過期,而是直接進行協商緩存。協商緩存後面會說到。

no-store表示禁止使用任何緩存政策,用戶端的每次請求都直接從伺服器擷取。也就是無緩存。

no-cache和no-store是互斥的,不能同時設定。

res.writeHEAD(200, {
   'Cache-Control': 'no-cache',
})      

還有private和public,他們也是cache-control的一組互斥屬性值,他們用來明确響應資源是否可被代理伺服器進行緩存。

publicb表示響應資源即可被浏覽器緩存又可以被代理伺服器緩存。private限制了響應資源隻能被浏覽器緩存。

對于應用程式中不會改變的檔案比如圖檔,js,css, 字型庫等通常可以使用公共緩存。

res.writeHEAD(200, {
   'Cache-Control': 'public, max-age=31600',
})      

除了max-age還有s-maxage,max-age表示伺服器告知浏覽器的響應資源的過期時長。一般使用它就足夠了了。

但如果是大型項目架構通常會涉及代理伺服器緩存,這就需要考慮緩存在代理伺服器上的有效性問題,這便是s-maxage存在的意義。

他表示緩存在代理伺服器上的過期時長,需要配合public來使用。

cache-control能作為expires的完全替代方案,目前expires隻作為相容使用。

協商緩存​

協商緩存就是在使用本地緩存之前,需要向伺服器發起一次GET請求,與之協商目前浏覽器儲存的本地緩存是否已經過期。​

協商緩存主要解決的問題就是在強制緩存下資源不更新的問題。

用戶端在擷取到本地緩存後需要向伺服器發送一次GET請求,這個請求的請求頭中包含if-modified-since字段,值是響應頭中的last-modified字段,也就是這個資源的最後修改時間。

也就是說用戶端請求資源的時候伺服器會傳回響應内容及内容的修改時間,修改時間存在last-modified字段中。

用戶端在請求的時候如果用戶端存儲了last-modified就将它的值放在if-modified-since字段中發送到伺服器。

伺服器接收到請求後通過比對前端傳過來的時間和資源的修改時間,如果二者相同則說明緩存未過期,就告訴浏覽器直接使用緩存中的檔案,如果過期了就傳回對應檔案并且将新的修改日期重新傳回。

用戶端繼續緩存新的修改時間。​

const http = require('http');
const fs = require('fs');
const url = require(''url');
http.creatServer((req, res) => {
   const { pathname } = url.parse(req.url);
   // 擷取檔案日期
   fs.stat(`www/${pathname}`, (err, stat) => {
     if (err) {
       res.writeHeader(404);
       res.write('Not Found');
       res.end();
     } else {
       if (req.headers['if-modified-since']) {
         const oDate = new Date(req.headers['if-modified-since']);
         const time_client = Math.floor(oDate.getTime() / 1000);
         const time_server = Math.floor(stat.mtime.getTime() / 1000);
         if (time_server > time_client) { // 伺服器的檔案時間大于用戶端
           sendFileToClient();
         } else {
           res.writeHeader(304);
           res.write('Not Modified');
           res.end();
         }
       } else {
         sendFileToClient();
       }
       function sendFileToClient() {
         let rs = fs.createReadStream(`www/${pathname}`);
         res.setHeader('Last-Modifyed', state.mtime.toGMTString());
         rs.pipe(res);
         rs.on('error', err => {
           res.writeHeader(404);
           res.write('Not Found');
           res.end();
         })
       }
     }
   })
}).listen(8080);      

上面的這種緩存方式存在兩個問題,首先他隻是根據資源最後的修改時間戳進行判斷,如果檔案沒有變更隻是儲存了一下修改時間也會變化。

其次辨別時間是秒,如果修改特别快在毫秒内完成(程式修改會有這樣的速度),那麼就無法識别緩存過期。

主要原因就是伺服器無法僅依據資源修改的時間戳識别出真正的更新,進而導緻緩存不準确。

為了解決這個問題從HTTP1.1規範開始新增了一個ETag的頭資訊, 實體标簽。

其内容主要是伺服器為不同資源進行哈希運算生成的一個字元串,該字元串類似于檔案指紋,隻要檔案内容編碼存在差異,對應的ETag标簽值就會不同,是以可以使用ETag對檔案資源進行更精準的變化感覺。​

const etag = require('etag')
res.setHeader('etag', etag(data));      

基于ETag發送的請求會在請求頭中以If-None-Match傳遞給伺服器。

在協商緩存中ETag并非last-modified的替代方案而是一種補充方案,因為他也存在一些問題。

首先,伺服器對生成檔案資源的ETag需要付出額外的計算開銷,如果資源體積較大,數量較多且修改較頻繁,那麼生成ETag的過程會影響伺服器的性能。

其次,ETag的值分為強驗證和弱驗證,強驗證根據資源内容進行生成,能夠保證每個位元組都相同。

弱驗證則根據資源的部分屬性值來生成,生成速度快但無法確定每次位元組都相同。并且在伺服器叢集場景下,也會因為不夠準确而降低協商緩存的有效性和校驗的成功性。

恰當的方式是根據具體的資源使用場景選擇恰當的緩存校驗方式。

緩存政策​

HTTP的緩存技術主要是為了提升網站的性能,如果不考慮用戶端緩存容量和伺服器計算能力的理想情況,我們當然希望用戶端浏覽器上的緩存觸發率盡可能高,留存時間盡可能長,同時還要ETag實作當資源更新時進行高效的重新驗證。

但實際情況往往是容量和計算能力都有限,是以就需要指定合适的緩存政策,利用有效的資源達到最優的性能效果。

明确需求邊界,力求在邊界内做到最好。

在使用緩存技術優化性能的過程中,有一個問題是不可逾越的,我們既希望緩存能在用戶端盡可能長久的儲存,又希望他能在資源發生修改時進行及時更新。這是兩個互斥的需求。

如何兼顧二者呢?

可以将網站所需要的資源按照不同的類型去拆解,為不同類型的資源制定相應的緩存政策。

首先html檔案是包含其他檔案的主檔案,為保證當其發生改變能及時更新,應該将其設定為協商緩存。​

cache-control: no-cache      

圖檔檔案的修改基本都是替換,同時考慮圖檔檔案的數量及大小可能對用戶端緩存空間造成不小的開銷,是以可以采用強制緩存且過期時間不宜過長。​

cache-control: max-age=86400      

css樣式表屬于文本檔案,可能存在的内容不定期修改,還想使用強制緩存來提高重用效率,故可以考慮在樣式表檔案的命名中增加指紋或版本号(一般為hash值),這樣發生修改後不同的檔案便會有不同的檔案指紋,也就是請求的url不同。

是以css的緩存時間可以設定長一些, 一年。​

cache-control: max-age=31536000      

js腳本檔案可以類似樣式表的設定,采用指紋和較長的過期時間,如果js中包含了使用者的私人資訊而不想讓中間代理緩存,可添加private屬性。​

cache-control: private, max-age=31536000      

緩存政策就是為不同的資源進行組合使用強制緩存,協商緩存及檔案指紋或版本号,這樣可以做到一舉多得,及時修改更新,較長緩存過期時間及控制所能進行緩存的位置。

緩存設定需要注意不存在适用于所有場景下的最佳緩存政策,凡是恰當的緩存政策都需要根據具體的場景考慮制定。

緩存決策要考慮下面幾種情況。

1)、拆分源碼,分包加載​

對于大型項目來說,代碼裡是非常龐大的,如果發生修改的部分集中在幾個重要的子產品中,那麼進行全量的代碼更新顯然比較冗雜,是以可以考慮在代碼建構過程中按照子產品拆分将其打包成多個單獨的檔案。

這樣在每次修改後更新提取時,僅需拉取發生改變的子產品代碼包,進而大大降低了需要下載下傳的内容大小。

2)、預估資源的緩存時效​

根據不同資源的不同需求特點來規劃響應的緩存更新失效,為強制緩存指定合适的max-age,為協商緩存提供驗證更新的ETag實體标簽。

3)、控制中間代理的緩存​

凡是涉及使用者隐私資訊的盡量避免中間代理的緩存,如果對所有使用者響應相同的資源,則可以考慮讓中間代理也進行緩存。

4)、避免網址的備援​

緩存是根據請求資源的URL進行的,不同的資源會有不同的URL,是以盡量不要将相同的資源設定為不同的URL。這會導緻緩存失效。

5)、規劃緩存的層次結構​

不僅請求的資源類型,檔案資源的層次結構也會對制定緩存政策有一定的影響。

CDN緩存

CND全程是Content Delivery Network,内容分發網絡,他是建構在現有網絡基礎上的虛拟智能網絡,依靠部署在各地的邊緣伺服器,通過中心平台的負載均衡,排程及内容分發等功能子產品,使使用者在請求所需通路的内容時能夠就近擷取,以此來降低網絡阻塞,提高資源對使用者的響應速度。

如果沒有CDN,假設我們的伺服器在北京,那麼海南的使用者通路我們的網站的時候需要不遠萬裡連結北京的伺服器擷取資源,這樣的速度是比較慢的。

CDN的工作原理就是就近響應,如果我們将資源放置在CDN上,當海南的使用者通路網站時,資源請求首先進行DNS解析,這個時候DNS會詢問CDN伺服器有沒有就近的伺服器,如果有就連結就近服務的IP位址擷取資源。

由于DNS伺服器将CDN的域名解析權交給了CNAME指向的專用DNS伺服器,是以使用者輸入域名的解析最終是在CDN專用的DNS伺服器上完成的。

解析出的IP位址并非确定的CDN緩存伺服器位址,而是CDN負載均衡器的位址。

浏覽器會重新向該負載均衡器發起請求,經過對使用者IP位址的距離,所請求資源内容的位置及各個伺服器狀态的綜合計算,傳回給使用者确定的緩存伺服器IP位址。

如果這個過程發生所需資源未找到的情況,那麼此時便會依次向上一級緩存伺服器繼續請求查詢,直至追溯到網站所在的跟伺服器并将資源拉取到本地進行緩存。

雖然這個過程看起來稍微複雜一些,但是使用者是無感覺的,并且能帶來比較明顯的資源加載速度的提升,是以對目前所有一線網際網路産品來說,使用CDN已經不再是一條建議而是規定。

CDN主要針對的是靜态資源并非适用網站所有的資源類型。所謂靜态資源就是不需要業務伺服器參與計算的資源,比如第三方的庫,js腳本檔案,css樣式檔案,圖檔等。

如果是動态資源比如依賴服務端渲染的html就不适合放在CDN上。

CDN網絡的核心功能包括兩點,緩存與回源,緩存指的是将所需的靜态資源檔案複制一份到CDN緩存伺服器上,回源指的是如果未在CDN緩存伺服器上查找到目标資源,或者CDN緩存伺服器上的資源已經過期,則重新追溯到網站根伺服器擷取相關資源的過程。

CDN的優化有很多方面,比如CDN自身的性能優化,靜态資源邊緣化,域名合并優化和多級緩存架構優化。這些可能需要前後端一起配合完成。

一般情況CDN會和主站域名區分,這樣的好處是避免靜态資源請求攜帶不必要的cookie資訊,還有就是考慮浏覽器對同一域名下并發請求的限制。

cookie的通路遵循同源政策,同一域名下的所有請求都會攜帶全部cookie資訊,雖然cookie存儲空間并不大,但是如果所有資源都放在主站域名下所有的請求全部攜帶資料量也是很大的。

是以将CDN伺服器的域名和主站域名進行區分是非常有價值的。

其次因為浏覽器對于同域名下的并發請求存在限制,通常Chrome的并發限制是6。可以通過增加類似域名的方式來提高并發請求數。

繼續閱讀