網絡也是前端性能優化的重要一環,網頁上的資源都要經過網絡來傳輸。
  優化網絡性能除了緩存和壓縮之外,還有就是協議和 CDN。
  HTTP 協議已經歷了多個版本,每個版本的出現其實就是為了解決已知的性能問題。
  目前市面上,有許多成熟的商業 CDN 服務,采用這些服務的網頁,在性能提升上也很可觀。
  2023-11-20 注意,HTTPS 是 HTTP 的安全版本,而目前有些瀏覽器默認會將 HTTP 的請求重定向到 HTTPS,這種多余的重定向完全可以避免。
  2023-11-27 據[HTTP Archive](https://httparchive.org/reports/state-of-the-web)網絡現狀報告指出,2023 年桌面和移動端頁面請求中位數分別是 71 和 67。
  由此可以看出,網頁會有大量時間花在 HTTP 請求上,所以對于請求的優化,能夠得到比較大的收益。
## 一、緩存
  Web 緩存可以自動將資源副本保存到本地,減少了客戶端與服務器之間的通信次數,加速頁面加載,降低網絡延遲,如下圖所示。
:-: 
  緩存的處理過程可以簡單的分為幾步:
1. 首先在緩存中搜索指定資源的副本,如果命中就執行第二步;
2. 第二步就是對資源副本進行新鮮度檢測(也就是文檔是否過期),如果不新鮮就執行第三步;
3. 第三步是與服務器進行再驗證,驗證通過(即沒有過期)就更新資源副本的新鮮度,再返回這個資源副本(此時的響應狀態碼為“304 Not Modified”),不通過就從服務器返回資源,再將最新資源的副本放入緩存中。
**1)強緩存**
  通用首部 Cache-Control 和實體首部 Expires 會為每個資源附加一個過期日期,相當于食品的保質期。
  在這個保質期內的資源,都會被認為是新鮮的,也就不會和服務器進行通信,如下圖所示。
:-: 
  這類在瀏覽器中直接判斷緩存是否有效的方式常被稱為強緩存。
  Expires首部會指定一個具體的過期日期(如下所示),由于很多服務器的時鐘并不同步,所以會有誤差,不推薦使用。
~~~
Expires: Fri, 24 Sep 2027 07:00:32 GMT
~~~
  Cache-Control 首部能指定資源處于新鮮狀態的秒數(如下所示),秒數從服務器將資源傳來之時算起,用秒數比用具體日期要靈活很多。
~~~
Cache-Control: max-age=315360
~~~
  當緩存的資源副本被同時指定了過期秒數和過期日期(Expires)的時候,會優先處理過期秒數。
  在Cache-Control首部中,有兩個比較混淆的值:no-cache 和 no-store。
  no-cache 字面上比較像禁止資源被緩存,但其實不是,no-store 才是這個功能。
  no-cache 可以將資源緩存,只是要先與服務器進行新鮮度再驗證,驗證通過后才會將其提供給客戶端,如下圖所示。
:-: 
  在通用首部中,還有個歷史遺留首部:Pragma。
  Pragma 首部用于實現特定的指令,它也有一個值為 no-cache,功能和 Cache-Control 中的相同,如下所示。
~~~
Cache-Control: no-cache
Pragma: no-cache
~~~
  2023-11-20 強緩存對于頁面性能的提升比較顯著,但是在必要時需要破掉緩存。
  例如剛發布好代碼,那么網頁就應該呈現最新的結構。
  最簡單的方式是為網頁地址包一層短鏈,在訪問短鏈重定向時,給 URL 增加一個時間戳參數,破壞 HTML 的緩存。
  而網頁中的腳本和樣式資源,可以保持強緩存。
**2)協商緩存**
  協商緩存需要與服務器通信后,才能判斷緩存是否過期,常用的驗證方法有兩種,第一種是日期比對法。
  服務器在響應請求的時候,會在響應報文中附加實體首部 Last-Modified,指明資源的最后修改日期,客戶端在緩存資源的同時,也會一并把這個日期緩存。
  當對緩存中的資源副本進行再驗證時,在請求報文中會附加 If-Modified-Since 首部,攜帶最后修改日期,與服務器上的修改日期進行比對,如下圖所示。
:-: 
  第二種是實體標記法,日期比對法非常依賴日期,如果服務器上的日期不準確,再驗證就會出現偏差,這個時候就比較適合用實體標記法。
  服務器會為每個資源生成唯一的字符串形式的標記(例如 52fdbf98-2663),該標記會保存在實體首部 ETag 中。
  在響應報文中附加 ETag,把標記返回給客戶端,客戶端接收并將其緩存。
  當對緩存中的資源副本進行再驗證時,在請求報文中會附加 If-None-Match 首部,如下圖所示。
:-: 
  只有當攜帶的標記與服務器上的資源標記一致時,才能說明緩存沒有過期,這樣就能返回緩存中的資源。
## 二、壓縮
  在請求首部中,Accept-Encoding 用于描述客戶端可接受的編碼格式,服務器按指定的編碼格式壓縮數據。
  在實體首部中,Content-Encoding 用戶描述內容編碼格式,告知客戶端用這個編碼格式解壓。
  常用的壓縮算法有 GZip 和[Brotli](https://github.com/google/brotli)。對于非媒體文件(例如 HTML、CSS、JavaScript 等)經過壓縮后,尺寸可減少 50%,甚至 80%。
  注意,由于圖像已經被壓縮,再用 GZip 和 Brotli 進行壓縮反而會使尺寸變大。
**1)GZip 壓縮**
  GZip 是一種基于 Deflate 算法的無損壓縮,Deflate 是同時使用了 LZ77 算法與哈夫曼編碼(Huffman Coding)的一個組合體。
  GZip 對于要壓縮的文件,首先使用 LZ77 算法,然后對得到的結果再使用哈夫曼編碼的方法進行壓縮。
  LZ77 算法會把數據中一些可以組織成短語(最長字符)的字符加入字典,然后再有相同字符出現采用標記來代替字典中的短語。
  例如 ABCCDEFABCCDEGH 通過 LZ77 算法可壓縮為ABCCDEF(7,6)GH,其中 7 是重復串起始字符 A 到前面串起始字符的距離,6 是重復串的長度(ABCCDE)。
  哈夫曼編碼使用變長編碼表對字符進行編碼,其中變長編碼表是通過一種評估字符出現機率的方法得到的,出現機率高的字符使用較短的編碼,反之則使用較長的編碼。
  例如 ABAABACD 字符串,經過哈夫曼編碼后,A 是 0,B 是 10,C 是 110,D 是 111,整個字符串變為二進制的 01000100110111,[算法過程](https://zhuanlan.zhihu.com/p/63362804)本文不再贅述。
**2)Brotli 壓縮**
  2015 年,Google 推出了 Brotli,這是一種全新的開源無損數據格式,并被現在所有現代瀏覽器支持。
  Brotli 也是一種基于 Deflate 算法的無損壓縮,但它會使用一個預定義的常用代碼術語詞典,該字典包含 6 種語言的 13000 多個詞。
  預定義的算法可以提升文件的壓縮比率,Brotli 能在 GZip 的基礎上,再壓縮 17-25% 的數據。
  如果瀏覽器支持 Brotli,那么在請求首部中會將 br 令牌包含在可接受的編碼列表中,如下所示。
~~~
Accept-Encoding: gzip, deflate, br
~~~
  GZip 的壓縮級別可指定 0~9 的整數來配置,而 Brotli 的范圍是 0~11,下圖是兩種壓縮算法針對同一文件,采用不同級別的壓縮[結果對比](https://www.coralnodes.com/gzip-vs-brotli/)。
:-: 
  注意,使用 Brotli 壓縮所有資源非常耗費計算資源和時間,在最高壓縮級別下,會讓服務器等待動態資源。
  服務器開始發送響應所花費的時間會抵消文件大小減少帶來的任何潛在收益,也就是說會延長 TTFB 的時間。
## 三、HTTP/2
  HTTP/2.0 是 HTTP/1.1 的擴展版本,主要基于 Google 發布的 SPDY 協議,引入了全新的二進制分幀層,如下圖所示。
:-: 
  保留了 1.1 版本的大部分語義,例如請求方法、狀態碼和首部等,由互聯網工程任務組(IETF)為 2.0 版本實現標準化。
  2.0 版本從協議層面進行改動,目標是優化應用、突破性能限制,改善用戶在瀏覽 Web 頁面時的速度體驗。
  HTTP/1.1 有很多不足,接下來列舉 4 個比較有代表性的,如下所列:
1. 在傳輸中會出現隊首阻塞問題。
2. 響應不分輕重緩急,只會按先來后到的順序執行。
3. 并行通信需要建立多個 TCP 連接。
4. 由于HTTP是無狀態的,所以每次請求和響應都會攜帶大量冗余信息。
**1)二進制分幀層**
  二進制分幀層是 HTTP/2.0 性能增強的關鍵,它改變了通信兩端交互數據的方式,原先都是以文本傳輸。
  現在要先對數據進行二進制編碼,再把數據分成一個一個的幀,接著把幀送到數據流中,最后對方接收幀并拼成一條消息,再處理請求。
  在 2.0 版本中,通信的最小單位是幀(frame),若干個幀組成一條消息(message),若干條消息在數據流(stream)中傳輸。
  一個 TCP 連接可以分出若干條數據流(如下圖所示),因此 HTTP/2.0 只要建立一次 TCP 連接就能完成所有傳輸。
:-: 
**2)多路通信**
  通信兩端對請求或響應的處理都是串行的,也就是按順序一個個處理。
  雖然在 HTTP/1.1 中新增了管道化的概念,讓客戶端能一下發送多個請求,減少了不必要的網絡延遲。
  不過那只是將請求的隊列順序遷移到服務器中,服務器處理還是得按順序來,所以本質上響應還是串行的。
  如果一定要實現并行通信,那么必須建立多條 TCP 連接,多個請求分別在不同的 TCP 通道中傳輸(如下圖所示),間接實現并行通信。
:-: 
  TCP是一種可靠的通信協議,中途如果出現丟包,發送方就會根據重發機制再發一次丟失的包。
  由于通信兩端都是串行處理請求的,所以接收端在等待這個包到達之前,不會再處理后面的請求,這種現象稱為隊首阻塞。
  HTTP/2.0 不但解決了隊首阻塞問題,還將 TCP 建立次數降低到只要 1 次。
  通信兩端只需將消息分解為獨立的幀,然后在多條數據流中亂序發送。
  最后在接收端把幀重新組合成消息,并且各條消息的組合互不干擾,這就實現了真正意義上地并行通信,達到了多路復用的效果。
  在CSS中,為了減少請求次數,會把很多小圖拼在一起,做成一張大的雪碧圖,現在借助多路通信后,不用再大費周章的制圖了,直接發請求即可。
**3)請求優先級**
  客戶端對請求資源的迫切度都是不同的,例如在瀏覽器的網頁(即HTML文檔)中,像 CSS、JavaScript 這些文件傳得越快越好,而像圖像則可以稍后再傳。
  在 HTTP/1.1 中,只能是誰先請求,誰就先處理,不能顯式的標記請求優先級。
  而在 HTTP/2.0 中,每條數據流都有一個 31 位的優先值,值越小優先級越大(0的優先級最高)。
  有了這個優先值,相當于能隨時建立一條綠色通道(如下圖所示),通信兩端可以對不同數據流中的幀采取不同策略,這樣能更好的分配有限的帶寬資源。
:-: 
**4)首部壓縮**
  HTTP是無狀態的,為了準確的描述每次通信,通常都會攜帶大量的首部,例如 Connection、Accept 或 Cookie。
  而這些首部每次會消耗上百甚至上千字節的帶寬。為了降低這些開銷,HTTP/2.0 會先用 HPACK 算法壓縮首部,然后再進行傳輸。
  HPACK 算法會讓通信兩端各自維護一張首部字典表,表中包含了首部名和首部值,如下圖所示。
:-: 
  其中首部名要全部小寫,并用偽首部(pseudo-header)表示,例如:method、:host或:path。
  每次請求都會記住已發哪些首部,下一次只要傳輸差異的數據,相同的數據只要傳索引就行。
**5)服務器推送**
  HTTP/2.0 支持服務器主動推送,簡單的說就是一次請求返回多個響應,如下圖所示。
:-: 
  但是在 2022 年,開啟服務器推送的網站只有[0.7%](https://almanac.httparchive.org/en/2022/http#http2-push)左右,比 2021 年還降低了 0.5% 左右。
  并且在[Chrome 106](https://developer.chrome.com/en/blog/removing-push/)中,默認情況下將禁用對 HTTP/2 服務器推送的支持。
  之所以如此不受待見,主要是以下兩個原因。
* 服務器無法獲知推送的資源是否在客戶端緩存中,若存在,則用于推送資源的帶寬就會被浪費。
* 服務端要支持推送,需要額外的開發和配置成本,這就有可能讓第三方服務器(例如 CDN 節點)無法進行推送。
  替代服務器推送的方案也已出現,第一個是[第四章](https://www.cnblogs.com/strick/p/17095094.html)的預加載(preload)資源,使用 HTML 代碼,如下所示。
~~~html
<link rel="preload" href="/css/style.css" as="style" />
~~~
  或者聲明 HTTP 首部(如下所示),雖然沒有主動推送快,但很安全,2022 年大約有[25%](https://almanac.httparchive.org/en/2022/http#alternatives-to-http2-push)的網頁在使用預加載。
~~~
Link: </css/style.css>; rel="preload"; as="style"
~~~
  另一個替代方案是[Early Hints](https://developer.chrome.com/blog/early-hints/),它是一個 HTTP 狀態碼(103),允許 Web 服務器在完整的 HTML 響應準備好之前告訴瀏覽器將來需要的資源。
  也就是說,瀏覽器在等待 HTML 的同時請求其他資源(例如 CSS、JavaScript、字體等),提前做一些工作,從而提升頁面加載速度。
  在下圖中,CSS 文件會在 HTML 響應完成后,再開始請求,兩者是串行的,總耗時 225ms。
:-: 
  而在下圖中,請求 HTML 后,就會響應 103 Early Hints,告知瀏覽器去加載 CSS 文件,從而將耗時縮小到 200ms。
:-: 
  在某些情況下,開啟 Early Hints 后,LCP 的耗時最大可以縮小 1 秒,下圖來源于 Shopify,使用的工具是[WebPageTest](https://www.webpagetest.org/)。
:-: 
## 四、HTTP/3
  在 2022 年 6 月 HTTP/3 協議實現了標準化。HTTP/3 棄用了 TCP 協議,改為基于 UDP 的 QUIC 協議來實現。
  之所以基于 UDP,是為了避免操作系統和中間設備(路由器、交換機等)的升級,讓新協議更容易推廣和部署。
  QUIC(快速UDP網絡連接)由 Google 于 2012 年研發,是一種可靠的網絡傳輸協議,雖然基于 UDP,但是仍然需要建立連接,并且握手也比較復雜。
  QUIC 還使用了流量和擁塞控制機制,防止發送方使網絡或接收方過載,比起 TCP 協議,QUIC 實現的這些功能更加智能,性能也更高,例如:
* 更快的連接建立,QUIC 允許 TLS 版本協商與加密和傳輸握手同時發生,從而減少延遲。
* 零往返時間(RTT),對于已經連接的服務器,客戶端可以直接跳過握手。
* 更全面的加密,QUIC 使用 TLS 1.3 握手方式,默認會提供加密。
  接下來會講解 QUIC 修復的兩個 HTTP/2 重大缺陷。
**1)TCP 隊首阻塞**
  無論是 HTTP/1.1 還是 HTTP/2.0 都基于 TCP 協議,當在傳輸過程中出現少量的丟包時,有可能會讓整個連接中的所有流都被阻塞。
  在下圖中,第一行描述的是 HTTP/1.1,創建了 3 條 TCP 連接,分別傳輸 A、B 和 C。
  第二行描述的是 HTTP/2.0,只創建了 1 條 TCP 連接,通過不同的數據流傳輸不同的資源。
  當第 3 個數據包丟失時,需要等待重傳的新包,TCP 在此期間不會處理其余數據。
:-: 
  第三行描述的是 HTTP/3.0,由于數據流之間互不影響,因此除了丟包的那條數據流會被阻塞之外,其余數據流會被繼續處理。
**2)網絡切換成本**
  在 TCP 中,當移動設備切換網絡時(例如從 WiFi 切換到蜂窩數據),由于 IP 地址發生了改變,因此連接就會失效,需要重連,如下圖所示。
:-: 
  TCP 出現的比較早,目前也沒有機制允許客戶端通知服務器自己的 IP 已改變。
  QUIC 引入了一個名為連接標識符(CID)的新概念,在兩端連接后,就會標識這個值,它具有唯一性并且在網絡切換時也不會改變。
  在下圖中,綠色方框就是 CID,有了 CID 之后就能避免重新創建連接。
:-: 
## 五、CDN
  CDN(Content delivery network,內容分發網絡)可以在全球分發各類資源,包括視頻流、文本文件等。
  并根據地理位置、網絡服務商等條件,將用戶的請求定位到離他路由最短、位置最近、負載最輕的邊緣節點上,實現就近定位,以此提升性能,如下圖所示。
:-: 
  就近訪問的能力依賴域名解析(DNS),當用戶訪問一個頁面時,瀏覽器根據域名找到對應的主機,此時就能解析到離自己最近的邊緣節點。
  2023-11-28 但是在實際商業運作中,也會根據實際情況,給予不同的調度方案,例如基于付費等級、成本和帶寬等。
  2023-11-20 除此之外,CDN 還能海外加速,無論靜態資源還是動態接口,都可以借助 CDN 進行加速。
**1)邊緣節點**
  邊緣節點也稱 CDN 節點、Cache 節點等,就是在網絡上建立的邊緣服務器,對用戶具有較好的響應能力和連接速度。
  當邊緣節點收到用戶的請求后,會先從 CDN 緩存中查詢數據,若沒有找到就進行回源,回源就是向存放文件的源服務器發出請求。
  CDN 不僅能對靜態資源加速,還支持動態加速,雖然邊緣節點無法直接獲取緩存好的數據,但是可以智能選擇最佳路由進行回源。
  2023-11-28 無論是靜態還是動態加速,本質上都是通過減小網絡延遲實現加速。
  只是前者優化資源距離,后者提供網絡專線傳輸和優化轉發,即優化 BGP 選路。
  BGP(Border Gateway Protocol)即邊界網關協議,用于在路由選擇域之間交換網絡層可達性信息的路由選擇協議。
  BGP 可能會因為運營商的原因而選一條比較遠的非最佳路線或直接限流,導致網絡延遲的發生。
  CDN 服務商可自建 BGP 網絡中間源,消除跨運營商網絡回源慢的問題,節省回源成本。
**2)邊緣計算**
  CDN 中的邊緣計算(Edge Computing)是指將原來在服務器中的運算移到離用戶最近的邊緣節點中完成。
  邊緣計算能夠減緩數據爆炸、網絡流量的壓力,并且在邊緣節點處理一部分數據后,能減少設備響應時間、降低延遲。
  物聯網、AR/VR場景、大數據和人工智能等行業對邊緣計算都有著極強的需求。
**3)白屏變化**
  之前公司項目的部分靜態資源采取了 CDN 加速,后面讓全部資源都走 CDN,白屏時間占比變化如下:
* 1 秒內的占比從 77.3% 最高提升至 78.7%
* 1 - 2 秒占比從 15.6% 最高提升至 18.7%
* 2 - 3 秒占比從 4% 最低下降至 1.8%
* 3 - 4 秒占比從 1.1% 最低下降至 0.6%
* 4 秒以上的占比從 2.1% 最低下降至 1.4%
## 六、API網關
  2023-11-20 API 網關作為請求的單一入口點,將請求分配給相應的服務,然后收集結果并將其傳遞給請求者,而不是讓客戶單獨請求訪問每項微服務。
:-: 
  使用 API 網關后,就能根據 URL 地址轉發到不同的服務,并且是收集數據進行分析的理想場所。
  有時候在請求接口時,會收到 5XX 狀態碼的響應,這些都是異常,嚴重影響用戶體驗,常見的 5XX 有:
1. 500:代碼出現嚴重錯誤,阻止其執行請求。
2. 502:轉發的服務不存在。
3. 503:服務器停機或超載,服務器是無法接受請求的狀態。
4. 504:轉發的服務超時,即無法在指定時間內響應。
  500 在代碼日志中會有明確的錯誤信息,所以修改起來比較容易。
  報 502 時,如果服務存在,那么就是網關問題,需要運維協助。
  當發生 504 時,可以確定是服務器在運行一段耗時的代碼,很有可能是某條慢查詢阻塞了數據庫,導致其他 SQL 語句都無法執行。
## 總結
  本文從 5 個方面闡述了網絡優化的細節。
  在第一節著重講解了緩存,HTTP 的緩存分為強緩存和協商緩存。
  在第二節對比了兩者壓縮算法:GZip 和 Brotli,并簡單介紹了 LZ77 算法和哈夫曼編碼的計算原理。
  在第三節中說明了 HTTP/2 協議的優勢,包括多路通信、請求優先級和首部壓縮,并說明了服務器推送不受歡迎的原因和替代方案。
  在第四節又說明了 HTTP/2 的缺陷,通過 HTTP/3 修復了這些缺陷,并提供了協議細節。
  在第五節中重點介紹了 CDN,包括它的原理、術語和邊緣計算的概念。
*****
> 原文出處:
[博客園-前端性能精進](https://www.cnblogs.com/strick/category/2267607.html)
[知乎專欄-前端性能精進](https://www.zhihu.com/column/c_1610941255021780992)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎閱讀。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020