整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          web前端開發常用上拉刷新demo,代碼可直接運行

          web前端開發常用上拉刷新demo,代碼可直接運行

          端開發過程中常用的上拉刷新demo,下拉刷新與此原理類似

          界面如下(有點丑的demo哈):

          代碼如下如示:

          <style>

          *{

          padding: 0;

          margin: 0;

          }

          .box{

          height: 100%;

          /* overflow: hidden auto; */

          }

          #refreshContainer{

          width: 90%;

          margin: 0 auto;

          }

          ul li{

          outline-style: none;

          outline: none;

          }

          #refreshContainer li{

          height: 60px;

          line-height: 60px;

          padding: 0 10px;

          background: green;

          margin: 10px auto;

          list-style:none;

          }

          </style>

          </head>

          <body>

          <div class="box">

          <ul id="refreshContainer">

          <li>111</li>

          <li>222</li>

          <li>333</li>

          <li>444</li>

          <li>555</li>

          <li>111</li>

          <li>222</li>

          <li>333</li>

          <li>444</li>

          <li>555</li>

          </ul>

          <p class="refreshText"></p>

          </div>

          </body>

          <script>

          (function(window) {

          // 獲取當前滾動條的位置

          function getScrollTop() {

          var scrollTop=0;

          if (document.documentElement && document.documentElement.scrollTop) {

          scrollTop=document.documentElement.scrollTop;

          } else if (document.body) {

          scrollTop=document.body.scrollTop;

          }

          return scrollTop;

          }

          // 獲取當前可視范圍的高度

          function getClientHeight() {

          var clientHeight=0;

          if (document.body.clientHeight && document.documentElement.clientHeight) {

          clientHeight=Math.min(document.body.clientHeight, document.documentElement.clientHeight);

          }

          else {

          clientHeight=Math.max(document.body.clientHeight, document.documentElement.clientHeight);

          }

          return clientHeight;

          }

          // 獲取文檔完整的高度

          function getScrollHeight() {

          return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);

          }

          var _text=document.querySelector('.refreshText'),

          _container=document.getElementById('refreshContainer');

          // 節流函數

          var throttle=function(method, context){

          clearTimeout(method.tId);

          method.tId=setTimeout(function(){

          method.call(context);

          }, 300);

          }

          function fetchData() {

          setTimeout(function() {

          _container.insertAdjacentHTML('beforeend', '<li>new add...</li>');

          }, 1000);

          }

          window.onscroll=function() {

          if (getScrollTop() + getClientHeight()==getScrollHeight()) {

          _text.innerText='加載中...';

          throttle(fetchData);

          }

          };

          })(window);

          </script>

          代碼可直接復制

          每日一分享,感謝所有關注的粉絲們

          讀一開始我們的H5頁面秒開率只有30%左右,現在我們的H5頁面秒開率達到了 75%。這中間巨大的差異究竟有哪些黑科技在里面?我們為什么要做H5頁面的秒開優化?我們的秒開指標是如何統計的?客戶端和H5是怎么配合做到 1+1>2的?監控是如何發現H5頁面可優化項的?我們又通過監控發現了哪些可優化的問題呢?

          1. 背景


          H5秒開優化是一個老生常談的問題,本文將逐步介紹如何通過客戶端 + H5 的優化手段(1+1>2)把秒開從 30% 提升到 75% ?后續接口預請求、客戶端預渲染以及預加載2.0上線后還會再次助力指標提升。


          為什么要做優化?

          Global Web Performance Matters for ecommerce 的報告中指出:

          • 47%的用戶更在乎網頁在2秒內是否完成加載。
          • 52%的在線用戶認為網頁打開速度影響到他們對網站的忠實度。
          • 每慢1秒造成頁面 PV 降低11%,用戶滿意度也隨之降低降低16%。
          • 近半數移動用戶因為在10秒內仍未打開頁面從而放棄。


          整體系統架構圖:


          2. 指標選擇


          首先講一下得物用來衡量秒開的指標 FMP,那為什么不選擇 FCP 或者 LCP 呢?FCP 只有要渲染就會觸發,LCP 兼容性不佳,得物希望站在用戶的角度來衡量秒開這件事情,用戶從點擊打開一個WebView到首屏內容完整的呈現出來的時間點就是得物定義的FMP觸發時機。


          指標清楚了之后,再來看一下完整的 FMP 包含哪些耗時。



          接下來將分為兩大部分進行介紹,客戶端優化部分和H5 優化部分。

          3. 客戶端優化


          通過 HTML 預加載、HTML 預請求、離線包、接口預請求、鏈接保活、預渲染等手段提升頁面首屏打開速度,其中預加載、預請求、離線包分別可提升10%左右的秒開。


          3.1 HTML預加載

          通過配置由客戶端提前下載好HTML主文檔,當用戶訪問時直接使用已經下載好的HTML文檔,以此減少HTML網絡請求時間,從而提升網頁打開速度。




          3.1.1 如何確定需要下載的頁面


          前人栽樹后人乘涼,得物App有很多的資源位,banner、金剛位、中通位等,這些位置顯示什么內容,早就已經是智能推薦算法產出的了,那么就可以直接指定這些資源位進行預加載。



          3.1.2 頁面緩存管理


          頁面被預加載之后,總不能一直不更新吧?那么什么時候更新頁面的緩存呢?

          • 預加載的頁面存放于內存中,關閉App緩存就會被清除
          • 通過配置過期時間人為控制最大緩存時間
          • 在頁面進入后發起異步線程去更新HTML文檔。


          被現實打臉:


          但是在后面的灰度過程中被現實狠狠的教育了一頓,發現有些SSR的頁面會涉及到狀態的變更,比如說:領劵場景。這些狀態都是經過SSR服務渲染好的,用戶在進入頁面時還沒有領劵,這個時候去更新HTML文檔,實屬更新了個寂寞,在用戶領劵之后關閉頁面再次進入,發現頁面中的狀態仍是讓用戶領劵,點擊領劵又告訴人家你已經領過了。



          改進措施

          1. H5 頁面在打開時針對狀態可能會發生變更的組件,再次請求接口獲取最新的狀態數據。
          2. 客戶端由進入頁面就更新HTML文檔修改為:關閉WebView時更新HTML文檔。


          3.1.3 線上收益效果


          至此問題也解決了,工程師的任務結束了嗎?如果你認為功能做上去就算結束,那么此時此刻請你一定要轉變思維,想一想我們的目標是什么?我們的目標是「提升秒開」,預加載只是一種提升秒開的手段,但是在功能做上去之后并不知道這個功能帶來了多少秒開的收益,因此在把功能開發完成上線之后,就要開始關注上線之后的結果,來看看預加載的性能表現如何。從下圖可以看到,預加載開啟狀態下可提升10%以上的秒開率。



          3.1.4 遇到的挑戰

          1. 預加載的頁面基本上都是 SSR 服務的頁面,預加載無形中造成了大量的請求,此時得物的SSR服務扛不住這么大的請求量;
          2. 即使SSR 服務扛得住,也會對后端整個服務鏈路造成壓力。

          (1)SSR服務擴容


          要解決服務器壓力問題,很自然就會想到增加機器,于是我們對SSR機器數量做了一次擴容,將機器數量提升了一倍,這個時候繼續嘗試擴大預加載的用戶數量,但是仍然無法抗住這么大的QPS,而且此時還引發了第二個問題,算法部門的服務器發出了告警,于是放量計劃又一次遇到了阻礙。


          (2)破局者 CDN


          利用CDN 服務器的緩存能力既可以減輕 SSR 服務器的壓力又可以減少后端服務鏈路的壓力,這么好的東西為什么不用呢?這里留個懸念,后面將H5優化部分會詳細介紹。


          (3)客戶端配合改造

          支持針對CDN域名進行全部開放預加載能力,針對非CDN域名保持原有放量比例。


          3.1.5 開屏頁預加載


          在這個過程中還分析了頁面的流量占比,發現開屏廣告來源的頁面流量占比也很高,那么是不是可以把開屏廣告的HTML文檔內容也給預加載下來呢?


          開屏頁面預加載策略


          1. 對預加載列表進行去重,開屏廣告列表中可能會存在重復的頁面,他們的背景圖和生效時間是不同的;
          2. 增加了生效時間相關配置,開屏廣告列表中存在于未來某段時間才會展示的頁面;
          3. 添加黑白名單控制,開屏廣告列表中可能會有第三方合作頁面,他們不希望預加載統計會造成PV時不準確。


          3.1.6 預加載展望


          既然可以提前下載好HTML,那是不是可以更進一步,提前把頁面內的資源加載好,這樣在打開一個頁面的時候可以減少大部分的網絡請求從而更快速的把內容呈現給用戶。這里還需要考慮如何跟下面講到的離線包進行協作。



          3.2 HTML預請求


          在WebView初始化同時,去請求HTML主文檔,等待HTML文檔下載完成 且 WebView初始成功后渲染,減少用戶等待時間,客戶端請求成功后,WebView加載本地 HTML,并保存以供下次使用。預請求HTML開啟狀態下可提升8%左右的秒開。


          預請求 VS 預加載


          本質上HTML預加載和HTML預請求的區別就是下載HTML文檔的時機不同, 預加載是在App啟動后用戶無任何操作的情況下就會去下載,但是預請求只會在用戶單擊打開H5頁面的時候才會去下載。如果用戶是第二次打開某個H5頁面,此時發現本地有已經下載好的HTML且尚未過期就會直接使用,這個時候的行為表現就跟預加載的功能是一致的了。


          3.2.1 遇到挑戰

          上線之后發現預請求只提升了2%左右的秒開,經過分析發現問題:

          1. 緩存有效時間太短,頁面過期時間只配置了10分鐘,也就是說在10分鐘之后用戶就要重新去下載一次,那能不能把緩存時間延長呢?
          2. H5頁面是沒有自更新能力,無法支持配置更長的緩存時間,跟預加載HTML問題一致。

          3.2.2 深入挖掘


          在本地用低端機對整個秒開耗時鏈路進行了分析,為什么要用低端機分析呢?低端機有個好處,天然的加上了慢放功能,可以最大程度發現問題。


          (1)安卓h5 頁面加載 與 原生布局填充并行執行



          從圖中可以看出h5 頁面加載之前 耗時 分布在 activityStart() 函數,該函數 包含了 onCreateView ,其中耗時最長是 布局填充 inflate(),因為 WebView 對象是提前創建好的,直接從對象池中取出的,所以耗時主要在 初始化過程,WebView 自身的初始化 WebViewChromiumFactoryProvider. startYourEngines (耗時 87 us,不到 1 ms),耗時還有 WebView 的一些其他初始化,jockey 的初始化 等等。


          而秒開的計算是包含了 View 初始化到 WebVIew url 加載 的耗時,從而發現了優化點,可以將Webview loadUrl 前置,h5 頁面加載 與 原生布局填充并行執行。在 onCreateView 時,創建 FrameLayout 進行返回,執行 WebView loadUrl 之后,主線程開始 對布局進行 inflate,布局加載成功后,將其 addView 到 FrameLayout 中,減少了 loadUrl 的阻塞時長。中高端機型有 15ms 左右優化,低端機型有 30 ~ 50 ms 優化 效果。


          (2)雙端下載HTML的時機提前至路由階段


          預請求HTML時機是在進入到 native 頁面中,這個時間點距離用戶單擊事件已經過去100ms,那么是否可以將下載HTML的時機提前呢?經過一番探索,最終選擇在路由階段進行攔截,既可以統一收口而且距離用戶點擊的時間間隔可以忽略不計。通過這種方式將下載HTML時機提前了平均80ms+。

          此時的流程變成了下面這樣。



          可能有的同學會問了,為什么不在用戶點擊的時候去下載呢?從用戶點擊到路由肯定還是有耗時的。


          1. 代碼層面不好維護,如果在點擊時就需要侵入到業務層面,入口千千萬,很難進行維護管理;
          2. 從點擊到路由這部分耗時在線下進行了性能測試,幾乎可以忽略不計。


          3.2.3 最終線上收益效果


          在上述問題解決后,將緩存時間修改為1天,發現預請求HTML開啟狀態下可提升8%左右的秒開,已經和預加載的效果相差不大了。


          3.3 離線包


          通過提前將H5頁面內所需的css、js等資源聚合在一個壓縮包內,由客戶端在App啟動后進行下載解壓縮,在后續訪問H5頁面時,匹配是否有本地離線資源,從而加速頁面訪問速度。



          3.3.1 安卓實現


          資源攔截這塊安卓這邊實現比較簡單,WebView支持 shouldInterceptRequest, 可以在該方法內檢測是否需要進行資源攔截,需要的話返回 WebResourceResponse 對象,不需要直接返回 null。



          3.3.2 iOS 實現


          但是在iOS 這邊遇到了一些困難,調研了以下方案:


          方案一:NSURLProtocol 攔截方式

          NSURLProtocol 攔截方式,使用WKBrowsingContextController和registerSchemeForCustomProtocol。通過反射的方式拿到了私有的 class/selector。通過把注冊把 http 和 https 請求交給 NSURLProtocol 處理。通過這種方式確實可以攔截請求,但是發現post請求的body會出現丟失的問題。而且NSURLProtocol一經注冊就是全局開啟。我們希望他只會攔截接入了離線包的頁面,但是沒有辦法控制他,他會攔截所有頁面的請求,包括第三方合作頁面,顯然這是無法接受的。


          方案二:通過CustomProtocol攔截請求


          在iOS 11及以上系統中, 擁有了加載自定義資源的API:WKURLSchemeHandler。

          可以修改當前頁面url為自定義 scheme 協議,比如:https://fast.dewu.com 修改為 duapp://fast.dewu.com 然后在客戶端內注冊該 scheme,前端配合修改頁面內所有的資源請求未自適應協議,如:src="//fast.dewu.com/xxx" 就可以實現攔截。但是在測試過程中發現,接口為了安全起見只允許白名單內的域名發起跨域請求,且無法配置多個域名,導致該方案無法繼續進行。


          方案三:hook handlesURLScheme



          仍然是使用 WKURLSchemeHandler 然后通過 hook WKWebview 的 handlesURLScheme 方法來支持 http 和 https 請求的代理。通過這種方式雖然可以攔截請求了,但是遇到了以下問題:


          (1)body丟失問題


          不過在 iOS 11.3 之后對這種情況做了修復處理,只有 blob 類型的數據會丟失。需要由JS來代理 fetch 和 XMLHttpRequest 的行為,在請求發起時將 body 內容通過 JSBridge 告知 native,并將請求交給客戶端進行發起,客戶端在請求完成后 callback js 方法。


          (2)Cookie 丟失、無法使用問題


          通過代理 document.cookie 賦值和取值動作,交由客戶端來進行管理,但是這里需要額外注意一點,需要做好跨域校驗,防止惡意頁面對cookie進行修改。


          3.3.3 遇到挑戰


          至此功能開發完成上線,先來一組線上收益數據,安卓開啟離線情況有有10%左右的收益,但是iOS開啟離線的反而秒開率更低。經過修復處理后iOS也可提升10%以上的秒開。



          安卓和iOS實現差異


          經過分析對比發現,安卓的攔截動作比較輕,可以判斷是否需要攔截,不需要攔截可以交給WebView自己去請求。


          但是iOS這邊一旦頁面開啟攔截后,頁面中所有的http和https請求都會被攔截掉,由客戶端發起請求進行響應,無法將請求交還給WebView自己去發起。


          iOS 緩存問題修復


          頁面中的資源經過客戶端請求代理后原本第二次打開WebView本身會使用緩存的內存,現在緩存也失效了,于是只能在客戶端內實現了一套緩存機制。


            1. 根據 http 協議來判定哪些資源可以被緩存以及緩存的時長
            2. 添加自定義的控制策略,僅允許部分類型的資源進行緩存


          3.3.4 離線包下載錯誤率治理


          從下圖可以看到離線包的下載錯誤率在 6% 左右浮動,這么高的下載錯誤率肯定是無法接受的, 經過一系列優化手段,把離線包下載錯誤率從6%左右浮動下降至 0.3%左右浮動。



          先來看下優化前的流程圖和問題點。



          通過埋點發現大量 unknown host 、網絡請求失敗、網絡連接斷開的情況。分析代碼發現下載未做隊列控制,會同時并發下載多個離線包,從而導致多個下載任務爭搶資源的情況。針對發現的問題點做出了以下優化:


            1. 下載失敗添加重試機制,并可動態配置重試次數用于緩解網絡請求失敗、網絡連接斷開的問題。
            2. 添加下載任務隊列管理功能,可動態配置并發下載數量,用于緩解不同下載任務爭搶資源的問題。
            3. 針對弱網和無網絡情況延遲到網絡良好時下載。
            4. 離線包下載支持 httpdns,用于解決域名無法解析的情況。


          下面是優化之后的流程圖:



          3.3.5 展望

          針對離線資源是直接存儲在磁盤上的,每次訪問都會有磁盤IO耗時,經過在低端機器上做測試發現這個耗時會在 0 - 10ms 之間進行波動,后面會把內存合理的利用起來,通過設置內存上限,文件數量上限,甚至是文件類型,并通過 LRU 策略進行內存文件的淘汰更新。

          3.4 接口預請求

          通過客戶端發起H5頁面首屏接口請求,遠比等待客戶端頁面初始化、下載HTML、JS下載執行的時機更提前,從而節省用戶的首屏等待時間。在本地測試過程中發現接口預請求可提前100+ms,用戶也就可以更快的看到內容。



          3.4.1 功能介紹


          客戶端會在App啟動后獲取配置,保存支持預請求的頁面地址及對應的接口信息,在用戶打開WebView時,會并行發起對應預請求的接口,并保存結果。當JS執行開始獲取首屏數據時,會先詢問客戶端是否已經存有對應的響應數據,如果此時已經拿到數據則無需發起請求,否則 js 也會發起接口請求并開啟競速模式。以下是整體流程圖:



          3.4.2 配置平臺


          那么客戶端怎么知道這個頁面需要請求什么接口呢?以及接口的參數是什么呢?那自然少不了配置平臺,它支持以下功能:


            1. 配置需要預加載的頁面 url并對應一個需要請求的 api url 以及參數
            2. 配置審核功能,避免錯誤配置發布上線


          3.4.3 QA 既然有了SSR服務端渲染為什么還需要接口預請求的功能?

          首先即使是在 SSR 的情況下,首屏內容中仍然可能有部分組件是骨架直出的,需要等待頁面渲染執行時才會去請求數據,另外還有一部分頁面是SPA的。針對這兩種情況都能做到很好的補充。


          3.5 預建連&鏈接保活

          開啟后DNS 90分位耗時從80ms降至0ms,TCP建連90分位耗時從65ms分位耗時降為0,DNS平均耗時從55ms降為4.3ms,TCP建連平均耗時從30ms降為2.5ms。


          3.5.1 網絡請求耗時分析


          通過上圖可以看到一個網絡請求在經過DNS解析耗時、TCP建連耗時、SSL建連耗時階段之后才能把請求發出去,那么是否可以節省這段時間的耗時呢?


          客戶端常用的網絡請求框架如OkHttp等,都能完整支持http1.1與HTTP2的功能,也就支持連接復用。了解了這個連接復用機制優勢,那就可以利用起來,比如在APP開屏等待的時候,就預先建立關鍵域名的連接,這樣進入相應頁面后可以更快的獲取到網絡請求結果,給予用戶更好體驗。在網絡環境偏差的情況下,這種預連接理論上會有更好的效果。


          3.5.2 實現方案


          可以通過對域名鏈接提前發起一個HEAD請求從而建立鏈接,網絡框架會自動將連接放入連接池。并在默認無操作5分鐘后進行釋放,在五分鐘內重復執行上述動作即可一直保持鏈接。


          另外這里需要注意下連接池的數量問題,如果連接池的數據太小,但是域名比較多的話,通過預建連保持的鏈接很容易就會被釋放掉,這就需要通過域名收斂或者調大連接池的數量來進行優化。


          3.5.3 線上收益


          那預建連會不會增加服務器的壓力呢?這個肯定是會的,首先會針對預建連功能本身就行灰度策略,在HTML頁面通過CDN托管后,直接針對 cdn 域名進行全量開啟,從而不用擔心 cdn 域名扛不住壓力。


          來看一下線上效果,通過下圖可以看到在開啟后DNS 90分位耗時從80ms降至0ms,TCP建連90分位耗時從65ms分位耗時降為0,DNS平均耗時從55ms降為4.3ms,TCP建連平均耗時從30ms降為2.5ms。



          3.6 預渲染


          客戶端提前通過WebView將頁面渲染好,等待用戶訪問時,可直接展示。從而達到瞬開效果。但是這種功能肯定不能對所有的頁面進行開放,而且存在一定的弊端。


            1. 會額外消耗客戶端資源,需要在主線程空閑時執行,并需要控制預渲染的頁面數量。
            2. 如果頁面一進入就會下紅包雨,這種頁面是不適合做預渲染的,需要進行規避。


          下圖【開學季】是業務上已經進行預渲染的H5頁面,可以看到在打開【開學季】頁面時,頁面已經渲染完畢,絲毫沒有等待過程。



          后面計劃把這種能力放大到通用的webview上,針對大促以及PV量高的頁面進行開放。

          4. H5 優化

          4.1 SSR服務端渲染


          SSR服務端渲染(英語:server side render)一般情況下,一個H5頁面的數據渲染完全由客戶端來完成,先通過AJAX請求到頁面數據并把相應的數據填充到模板,形成完整的頁面來呈現給用戶。而服務端渲染把數據的請求和模板的填充放在了服務端,并把渲染的完整的頁面返回給客戶端。



          SSR對于秒開有平均15%的提升,既然是服務端渲染,就會對服務器造成壓力,尤其是在預加載HTML功能開啟后,那得物是如何解決的呢?


          4.1.1 前期優化內容

            1. 接口緩存:node服務向ctx中注入redis實例,業務方在服務端渲染的邏輯中處理接口相關的緩存,這其中涉及:配置文件下發、ab接口。
            2. 靜態頁面緩存:由于頁面完成是無接口交互,并且所有用戶展示都是一樣的,由renderToHtml生成靜態html資源,寫入緩存。
            3. 無用戶狀態頁面緩存:此類頁面大多數情況下展示內容都是一樣的,服務端請求的數據也都是一致的,服務端處理的時候根據是否有用戶登陸狀態判斷是否需要緩存。
            4. 千人千面接口內容由SSR修改為CSR,并顯示骨架圖,由于千人千面內容都是由算法接口提供,且算法接口本身響應較慢,因此通過這種方式可以減少服務端響應耗時,且能更快速的給用戶展示內容。


          4.1.2 破局者CDN


          通過這么多優化手段仍然無法滿足預加載的需求,并且通過分析發現網絡階段耗時較長,最終還是搬出了CDN這個大殺器,一直沒上CDN的原因有很多,主要有以下幾方面:


          (1)得物的頁面是千人千面的每個人看到的內容都不同

          通過上述優化4即可解決,將原本SSR渲染的內容修改CSR,由于已經上了CDN了,后續計劃將這部分內容再次修改回SSR,這樣用戶可以更快的看到商品而不是骨架,然后通過 CSR 的方式來更新內容。


          (2)頁面狀態變更,無法及時更新緩存

          這個問題在上述客戶端預加載優化部分已經有解決方案了,可以通過在頁面打開后針對有需要的組件再次請求接口刷新數據以確保數據的準確性,但是這部分工作量也是比較大的,梳理出來的需要刷新狀態的組件就有30+,而且之后開發的組件都需要考慮狀態更新的事情。


          (3)HTML模板內容變更無法及時更新

          引起模板內容變更的地方有兩處,第一個場景就是在搭建器場景下,運營可以動態修改模板內容導致頁面結構變更(低頻次),第二個場景是項目發版后模板內容需要更新(高頻)。


          這個問題可以通過在感知到內容變更時自動調用CDN服務商的刷新緩存接口來更新CDN緩存內容。

          4.2 預渲染HTML


          通過puppeteer將SPA頁面渲染出來并將HTML文檔進行保存,配合上述頁面刷新策略,并將HTML通過CDN進行托管,讓你的 SPA 頁面 像 SSR 一樣絲滑。


          主要實現方案是通過基于webpack的插件prerender-spa-plugin,并配置需要預渲染的路由,這樣經過打包之后就會產出對應路由的頁面。方案本身是通用的,但是每接入一個頁面都需要人工check。


          4.3 不起眼的css包大小優化


          眾所周知 css 加載會阻塞HTML渲染,最終將首屏公共css從118kb縮減至38kb,下圖通過 chrome 模擬弱網環境下的SSR頁面加載時序圖。從圖中可以看出 styles.fb201fce.chunk.css 下載耗時 18s,阻塞了頁面的渲染,HTML 主文檔耗時 2.38s 就已經下載完成了,但是實際渲染時間卻是在 20s 之后。



          優化思路也比較單間,將首屏所需要的css 文件通過內聯方式內嵌到HTML中,由SSR服務一并返回,并對css文件進行拆分,按需加載。


          思路有了,接下來就看怎么去實現了,最初嘗試了MiniCssExtractPlugin 插件他可以把css分成單獨的文件,并且每一個js會對應生成一個css文件,但是他需要建立在webpack5之上,然而項目中使用的next版本是9.5,于是就想著升級到最新版next12,升級后發現,在構建中其他包各種報錯,發現有些包并不支持最新的next12,在嘗試一天的修復之后,仍未解決,且升級到最新版不確定是否會引發其他穩定性問題,暫且擱置尋找其他方法。


          經過不懈的努力,通過閱讀 next 源碼發現了端倪,發現在打包時將所有的公共css通過 splitChunks 進行分組,由于項目中組件都是動態引入的,這里直接在 next.config.js 中修改webpack打包參數,將 splitChunks.cacheGroups.styles 配置刪除,使用默認的 chunks: async 配置,即可實現按需引入。


          4.4 圖片優化

          (1)避免圖片src為空

          雖然src屬性為空字符串,但瀏覽器仍然會向服務器發起一個HTTP請求,尤其是在SSR服務器壓力扛不住的情況下,因此這里需要特別注意一下。


          (2)圖片壓縮以及格式選擇

          WebP 的優勢體現在它具有更優的圖像數據壓縮算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的圖像質量;同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG 和 PNG 上的轉化效果都相當優秀、穩定和統一。

          通過向圖片服務器傳遞參數選擇合適的分辨率。


          4.5 細節優化


          (1)打包優化

          • 頁面組件拆分,優先加載首屏內容所需資源
          • webpack splitchunks 有效拆分公共依賴,提高緩存利用率
          • 組件按需加載
          • Tree Shaking 縮減代碼體積


          (2)非關鍵js、css延遲加載

          • defer、async、動態加載js
          • iOS 設備延遲加載 js


          (3)媒體資源加載優化

          • 圖片、視頻懶加載
          • 資源壓縮、通過向圖片服務器傳遞參數選擇合適的分辨率


          (4)其他資源優化

          • 數據埋點上報延遲發送,不阻塞 onload 事件觸發
          • 自定義字體優化,使用 fontmin 生成精簡的字體包


          (5)頁面渲染優化

          • 頁面渲染時間優化
            • SSR頁面首屏 css 內聯(Critial CSS)
          • 合理使用 Layers
          • 布局抖動優化:提前定好寬高
          • 減少重排重繪操作


          (6)代碼層面優化

          • 耗時任務分割
            • 通過 Web Worker 減少主線程耗時
          • 通過 RAF 回調,在線程空閑時執行代碼邏輯
          • 避免 css 嵌套過深

          5. 監控


          為了幫助開發者更好地衡量和改進前端頁面性能,W3C性能小組引入了 Performance API ,其中Navigation Timing API 實現了自動、精準的頁面性能打點。得物前端性能監控指標也都是從 Performance API 中獲取數據進行上報統計分析的。



          5.1 系統架構


          SDK 數據采集完畢后,會上報到 阿里云 sls 日志平臺,隨后通過 flink 實時消費清洗數據后落庫至 clickhouse 中,平臺后端通過讀取 clickhouse 數據并做各種聚合處理后使用。



          5.2 指標大盤

          做優化之前首先要建立監控指標,互聯網稱之為抓手,沒有監控指標的情況下,任你怎么優化,都不知道優化的效果怎么樣,更不知道下一步該做什么?以及還有哪些問題沒解決。因此優化之前指標先行,當然一定要指標的準確性。


          指標大盤主要包含以下功能:

            1. 可快速查看某段時間內的版本、設備廠商、設備名、設備系統版本以及網絡占比情況,還可以根據這些字段進行篩選排查。
            2. 中間區域展示了比較關注的整體以及活動頁面的客戶端耗時和H5秒開耗時的情況。
            3. 底部區域展示了各業務域的秒開耗時情況。
            4. 這里同時展示了平均耗時90分位耗時,平均耗時有個弊端就是很容易被平均,大家應該都有被平均的經歷。90分位耗時這里簡單講解一下:意思就是有90%的訪問耗時是低于90分位耗時的,以此類推 50 分位就是有50%的訪問耗時是低于50分位的,分位值是將所有的耗時數據從小到大排序后得出的。


          5.3 白屏監控


          在正常情況下,完成上述的優化措施后用戶基本是可以秒開 H5 頁面的了。但異常情況總是會有的,用戶的網絡環境和系統環境千差萬別,甚至 WebView 也可能發生內部崩潰。當發生問題時,用戶看到的可能就直接只是一個白屏頁面了,所以進一步的優化手段就是需要去檢測是否發生白屏以及相應的應對措施。


          檢測白屏最直觀的方案就是對 WebView 進行截圖,遍歷截圖的像素點的顏色值,如果非純色的顏色點超過一定的閾值,就可以認為不是白屏。首先獲取包含 WebView 視圖的 Bitmap 對象,然后把截圖縮小到指定的分辨率大小如:100*auto,遍歷檢測圖片的像素點,當非純色的像素點大于 5% 的時候就可以認為是非白屏的情況,但是還有很多列外的情況,我們通過圖片識別技術對截圖進行分析,可以很好的感知當前是否白屏、是不是在loading、是不是特殊頁面等。


          白屏是一個重要的指標,我們針對整體白屏率快速拉升、新增白屏頁面發出告警通知,便于開發人員及時介入開始排查問題。


          5.4 性能問題發現


          主要通過 CDN 未覆蓋監控、http請求監控、網絡監控(加載失敗、耗時異常、傳輸大小異常)、圖片監控(未壓縮、分辨率異常)等監控手段發現頁面中的潛在問題,同時還提供了問題分析能力,在問題分析頁面輸入頁面url地址即可幫助您發現問題并給出修改建議。


          5.4.1 CDN未覆蓋監控


          CDN的重要性不言而喻,它可以加速資源訪問速度,從而提升用戶體驗,我們通過對線上埋點數據分析,找出CDN未覆蓋的資源列表,從而推動各業務同學優化。


          5.4.2 HTTP請求監控

          為什么要監控HTTP請求呢?我們先來看一下HTTPS相對于HTTP新增的特點:


          1. 內容加密:采用混合加密技術,中間者無法直接查看明文內容
          2. 驗證身份:通過證書認證客戶端訪問的是自己的服務器
          3. 保護數據完整性:防止傳輸的內容被中間人冒充或者篡改


          那么HTTP就容易被中間人查看到內容,甚至被篡改,既然如此為了我們服務的安全性就需要對現有的http協議統一進行升級改造,那就需要監控去發現。


          5.4.3 網絡監控


          某些頁面秒開率低,那就要分析一下原因,是不是這個頁面的接口響應比較慢呢,還是說頁面本身有請求比較大的資源?如果發生網絡請求失敗的情況也要第一時間感知,不能被動等待用戶反饋。


          5.4.4 圖片監控


          包含圖片未壓縮、圖片分辨率異常、圖片傳輸大小大于 300kb 異常、動圖資源傳輸大小大于 1M 異常功能。


          5.4.5 頁面問題分析


          上面列出來一堆功能,對于業務的同學可能比較煩惱,我一個頁面具體有哪些問題呢?你不能讓我去上面的功能里面一個個看,哪個異常是我負責的頁面的吧?這個功能本身就行將現有的功能利用起來,通過一個頁面path進行聚合分析。



          5.5 異常監控


          H5異常一直是使用 sentry 進行監控的,但是sentry系統因缺乏同PV、DAU數據的關聯性,因此無法衡量產品異常發生后所帶來的嚴重程度。在業務域關聯上的缺失導致異常問題無法根據業務域進行劃分。用戶行為日志也尚未與Native 端側打通,在問題分析時容易遇到上下文不全的瓶頸。還有一個問題是sentry會有限流措施,當qps較高時會丟棄一部分異常數據。



          由于sentry已經可以幫助我們進行一定的問題排查分析能力,我們不打算做sentry同樣的功能,而是做sentry不支持的部分,針對上述問題我們設計了以下功能:


          • 異常問題指標衡量
            • 增加異常率、頁面異常率、影響用戶率趨勢
            • 增加問題多維度(系統版本、APP版本、H5發布版本、網絡等)下的分布占比、業務域劃分
          • 異常問題聚合能力增強(聚合問題能力增強)
            • 異常列表支持最新新增、Top PV、異常次數、影響用戶數排序
            • 區分三方sdk異常、接口異常等不同異常類型劃分


          6. 未來展望&總結


          雖然目前秒開率已經做到了75%以上,但是同時我們還有一個重要的指標,90分位耗時,致力于提升末尾用戶H5頁面使用體驗,在90分位優化完成后,可能會考慮繼續深入優化95分位耗時。

          最后感謝那些為得物H5頁面秒開做出貢獻的同學,感謝H5團隊,同學們都很棒,各種優化手段和想法層出不窮。


          至此我們系統的講解了背景以及從指標建立到秒開優化上線的全過程,全文分成了三個部分,客戶端、H5、以及監控。如果閱讀本文對您有所收獲,麻煩您動一動發財的小手點個贊吧!如果閱讀完還意猶未盡或者有什么問題和想法歡迎留言區評論交流。


          最后奉上整體優化腦圖:


          *文/徐銘

          歡迎關注「得物技術」微信公眾號,每周一三五晚18:30更新技術干貨
          要是覺得文章對你有幫助的話,歡迎評論轉發點贊~

          端開發

          軟件開發是一個高度專業化的職業分工,根據所使用的編程語言的不同,會細分出多種崗位:前端開發、后端開發、客戶端開發、iOS開發、Android開發、數據庫開發等等,具體到每一個崗位,工作中常用的工具軟件也存在著差別。

          就前端開發或前端工程師來說,前端技術的發展可以說是日新月異,新工具和框架層出不窮,以應對不斷變化的市場需求。在這篇文章中,我們將介紹前端開發中常用的8大類工具軟件,以及每個類別下面最具代表性的產品,總計17款工具軟件,幫助各位在選擇前端工具時減少糾結,提高工作效率和開發質量。

          • 代碼編輯工具:WebStorm、VS Code
          • 代碼版本控制工具:Git、SVN
          • 代碼包管理工具:npm、Yarn
          • 筆記工具:boardmix、Evernote、Typora
          • 前端構建工具:Webpack、Vite
          • 調試工具:瀏覽器開發工具
          • 響應式設計工具:Bootstrap、Material-UI
          • 瀏覽器插件:easy auto refresh、FeHelper(前端助手)、Vue Devtools

          前端開發常用的17款工具軟件

          01 代碼編輯工具

          選用哪個編輯器來寫代碼,與 IT 界由來已久的一個話題「XX語言是最好的編程語言」有點像,每個人都有自己的想法。工具本身本身并不是最重要的,能否寫出簡潔、優雅的代碼,主要是取決于自己的個人經驗和邏輯能力,代碼思路沒梳理清楚,代碼編輯器雕出花來也沒用哇。

          ① WebStorm

          WebStorm,最智能的 JavaScript IDE 工具,是一個用于 JavaScript 和相關技術的集成開發環境。與 JetBrains 推出的其他 IDE 一樣,它使你的開發體驗更加愉快,使日常工作自動化,并幫助你輕松處理復雜的任務。

          ② VS Code

          VS Code,微軟推出的開源代碼編輯器,支持 Windows、macOS 和 Linux 系統。單憑免費 + 輕量 + 大廠背景(光環)這些點,想必就吸引了不少人。

          VS Code 雖然不像 WebStorm 定位為 IDE 工具,但它可以內置的擴展功能、可以安裝不同的插件,也讓 VS Code 在保證靈活性的同時,也能變得足夠強大——軟件本身沒有的功能,可以用插件來彌補。

          除了這里介紹的兩個工具,其他可選的代碼編輯器有 Sublime Text、Atom、Vim、CodePen(在線代碼編輯工具) 等,這里就不展開介紹了。

          02 代碼版本控制工具

          ① Git

          Git 是目前最流行的分布式版本控制系統,它具有高效的分支管理和合并功能,可以支持大型項目的開發和維護。Git 使用簡單,有強大的命令行工具和圖形界面工具,例如 Git Bash 和 Sourcetree。程序員可以通過 Git 進行代碼的提交、拉取、推送等操作,同時還可以方便地查看代碼的歷史記錄和比較不同版本之間的差異。

          ② SVN

          SVN 是一種集中式版本控制系統,與 Git 不同,它使用集中式的服務器來存儲代碼,并且需要通過網絡連接才能進行代碼的提交和更新。SVN 具有較為簡單的操作和較好的穩定性,適合小型項目和對分支管理要求不高的團隊使用。程序員可以通過 TortoiseSVN 等圖形界面工具來進行 SVN 的操作,也可以通過命令行工具來進行更高級的操作。

          03 代碼包管理工具

          當涉及前端開發時,npm(Node Package Manager)和Yarn都扮演著至關重要的角色,它們是包管理工具,用于管理JavaScript庫和框架的依賴關系。

          ① npm

          npm是JavaScript世界中最常見的包管理工具之一,它是Node.js的官方包管理器,囊括了各種用于前端和后端開發的包。npm提供了功能強大且靈活的命令行界面,用于執行各種操作,包括安裝依賴項、更新包、運行腳本等。這使得前端開發者可以在命令行中快速而高效地完成各種任務。

          ② Yarn

          Yarn是JavaScript世界中的另一款重要的包管理工具,尤其受到前端開發者的歡迎。它在某些方面與npm相似,但也有自己的獨特特點:

          出色的性能:Yarn被設計為更快的包管理工具。它支持并行下載和安裝依賴項,從而提高了性能。這對于大型項目和具有多個依賴項的應用程序特別有幫助,能夠顯著加快構建和安裝速度。

          版本鎖定:Yarn使用yarn.lock文件來確保依賴關系的一致性。這意味著在不同開發環境中的版本沖突問題將被大大減少,確保您的項目在不同的計算機上以相同的方式運行。

          易于使用:Yarn的命令和操作更加直觀,使新手可以更快地上手。它提供了更友好的命令行交互和更清晰的日志輸出,幫助開發者更好地理解正在發生的事情。

          自定義包源:Yarn允許配置自定義包源,這對于需要使用特定鏡像或私有存儲庫的項目非常有幫助。這為開發者提供了更大的靈活性,以適應不同的項目要求。

          Yarn提供了一種高性能、可靠且易于使用的包管理工具,適用于前端開發項目。它的版本鎖定和離線模式等功能使其在復雜的項目環境中尤為有用。選擇使用npm還是Yarn取決于您的項目需求和個人偏好,但無論您選擇哪一個,都可以提高包管理的效率。

          04 筆記工具

          ① boardmix

          boardmix是一款跨平臺的筆記工具,有點接近微軟推出的OneNote,但功能比OneNote更強大。

          boardmix可以幫助程序員將各種信息和想法整理成文檔、文檔卡片、思維導圖、便簽等形式,支持文字、代碼塊、圖片、表格、鏈接等多種內容的插入,還可以通過標簽和容器來組織和分類筆記。此外,boardmix還支持手寫和音視頻通話功能,方便程序員在會議或討論中進行記錄。

          *跨平臺筆記工具boardmix

          跨平臺筆記工具boardmix的特點:

          1. 實時保存,跨平臺自動同步
          2. 融合了代碼塊、思維導圖、流程圖、文檔、看板、表格、PPT等功能,支持在筆記中添加多種形式的內容,滿足多樣化的記錄或協作需求
          3. AIGC創作功能,可用AI快速生成生產級別的代碼
          4. 兼容多種第三方應用,如Figma、Pixso、騰訊會議、騰訊文檔、Notion、高德地圖、億圖圖示、金數據等,在一個無限畫布上同時使用或瀏覽多個應用
          5. 文檔支持富文本編輯和簡單的Markdown編輯兩種模式
          6. 支持導出多種文件,包含Markdown、圖片、PDF、CSV、JSON、TXT等

          ② Evernote

          Evernote是一款功能強大的云端筆記應用,它可以幫助程序員隨時隨地記錄和整理自己的想法、代碼片段、技術文檔等。Evernote支持多種格式的筆記,包括文字、圖片、音頻和視頻等,還可以通過標簽和筆記本來組織和分類筆記。

          ③ Typora

          Markdown筆記軟件是程序員常用的一種筆記工具,它使用簡潔的標記語法來編寫和格式化文本,可以幫助程序員記錄和整理代碼片段、技術文檔、項目文檔等信息。

          市面上可選擇的Markdown筆記軟件非常多,其中最具代表性的是Typora。Typora是一款跨平臺的Markdown編輯器,它提供了實時預覽功能,可以讓程序員在編寫的同時實時查看渲染后的效果。Typora支持多種導出格式,如PDF、HTML等,方便用戶分享和發布筆記。

          05 前端構建工具

          ① Webpack

          說到前端構建工具,不得不提許多前端工程師都逃不過的 Webpack,我們先來看一下 Webpack 中文文檔對 Webpack 的介紹:

          Webpack,是一個用于現代 JavaScript 應用程序的靜態模塊打包工具。當 Webpack 處理應用程序時,它會在內部從一個或多個入口點構建一個依賴圖(dependency graph),然后將你項目中所需的每一個模塊組合成一個或多個 bundles,它們均為靜態資源,用于展示你的內容。

          如果是第一接觸 Webpack 的朋友,看完這段介紹應該還是一臉懵逼,不知道 Webpack 到底能用來干什么。

          隨著前端變得越來越復雜,我們在使用框架編寫項目時,可能會有下面這些行為:

          • 引入各種各樣的模塊來實現模塊化開發
          • 使用一些高級特性來加快開發效率或提升安全性,例如使用 Sass、Less 等方式來編寫 CSS 樣式代碼
          • 開發完成后需要對代碼進行壓縮、合并等,優化頁面的打開速度

          以其中的 Sass 樣式文件為例,瀏覽器默認情況下只能渲染 CSS 文件,而不認識這里的 Sass 文件,要想將 Sass 文件轉換為瀏覽器可讀取的 CSS 文件,就需要用到 Webpack,實現 Sass 到 CSS 的自動轉換。

          除了 Sass 文件,Webpack 還可以將 ES6 語法的 JS 代碼轉換為 ES5,將 TypeScript 代碼轉換為 JavaScript,也可以對圖片、字體等進行打包優化,減少文件占用的空間,可以這么說,有了 Webpack,原先我們要處理的一系列問題,都可以讓它自動幫我們完成。

          ② Vite

          Vite是一個快速、現代的前端構建工具,專為Vue.js框架設計,但也支持其他現代JavaScript框架和庫。

          快速開發:Vite是為了提高前端開發的速度而創建的。它利用ES模塊的特性,將開發服務器保持在本地開發環境中,實現了快速的冷啟動和熱模塊替換(HMR),這意味著您可以在幾乎沒有構建等待時間的情況下迅速查看更改。

          Vue 3支持:Vite是Vue 3的官方開發工具,它緊密集成了Vue 3的特性和生態系統。這使得使用Vite來構建Vue 3應用程序變得非常高效。

          支持其他框架和庫:盡管最初是為Vue.js設計的,但Vite也支持React、Preact、Svelte等現代JavaScript框架和庫。這意味著開發者可以在不同項目中使用Vite,而不僅僅限于Vue.js。

          插件系統:Vite具有靈活的插件系統,可以通過插件擴展其功能。這使得開發者可以根據項目需求輕松自定義構建過程。

          構建優化:盡管Vite在開發環境下快速運行,但它也提供了用于生產構建的工具,以優化和壓縮代碼,減小文件大小。

          開發者體驗:Vite注重開發者體驗,提供清晰的錯誤消息和有用的調試工具,以幫助開發者更輕松地診斷和解決問題。

          Vite是一個面向現代前端開發的快速工具,它通過利用ES模塊、本地開發服務器和HMR等特性,提供了出色的開發體驗。它適用于Vue.js項目,同時也能夠支持其他流行的JavaScript框架和庫,使其成為前端開發者的強大工具。

          06 調試工具

          瀏覽器開發工具

          對于前端Web開發,瀏覽器開發工具是非常有用的調試工具。瀏覽器開發工具可以幫助程序員查看網頁的HTML結構、CSS樣式和JavaScript代碼,同時還可以監控網絡請求和調試JavaScript代碼。常見的瀏覽器開發工具有Chrome開發者工具、Edge開發者工具、Firefox開發者工具等。

          07 響應式設計工具

          ① Bootstrap

          Bootstrap是由Twitter開發的開源前端框架,它提供了一組CSS和JavaScript工具,用于創建現代、響應式的Web頁面和Web應用程序。

          ② Material-UI

          Material-UI是一個基于Google的Material Design理念的React組件庫。它旨在提供Material Design的外觀和感覺,以及與React的深度集成。

          08 瀏覽器插件

          ① easy auto refresh

          easy auto refresh,一個自動刷新瀏覽器頁面的插件,比較適合剛接觸前端的新人。

          在未使用前端框架或 Webpack 打包項目的情況下,每當我們想預覽代碼在瀏覽器中的運行效果,都需要手動刷新頁面,操作起來比較繁瑣。

          對于這個問題,可以使用這里介紹的瀏覽器插件 ,提前設置一個刷新的頻率,就能實現頁面的自動刷新。

          ② FeHelper(前端助手)

          FeHelper,一個可以格式化 json 數據的瀏覽器插件。

          在瀏覽器中查看服務器返回的 json 數據時,由于它們被壓縮過,去除了數據中原有的換行,會呈現為「亂成一團」的狀態,不便于查看我們想要的數據。

          而 FeHelper 插件,就可以讓被壓縮后的 json 數據還原為壓縮前的狀態,即對數據進行格式化,美化壓縮后的數據文件,讓我們可以更方便地查看服務器返回的數據。

          ③ Vue Devtools

          Vue Devtools(Vue.js開發者工具)是專門為Vue.js開發者設計的瀏覽器擴展,它提供了強大的調試和開發工具,有助于更好地理解、分析和調試Vue.js應用程序。


          主站蜘蛛池模板: 亚洲变态另类一区二区三区| 女女同性一区二区三区四区| 日韩国产精品无码一区二区三区| 中文字幕一区二区三区免费视频| 一区二区三区四区视频在线| 亚洲乱码日产一区三区| 国产一区二区电影| 久久人妻无码一区二区| 亚洲熟女少妇一区二区| 亚洲一区中文字幕| 狠狠做深爱婷婷综合一区 | 国产内射999视频一区| 在线观看精品一区| 久久精品国产亚洲一区二区| 亚洲欧洲日韩国产一区二区三区| 国产成人免费一区二区三区| 99久久人妻精品免费一区| 色一情一乱一区二区三区啪啪高| 无码中文人妻在线一区| 国产a久久精品一区二区三区| 亚洲一区二区精品视频| 色久综合网精品一区二区| 国产精品第一区揄拍| 精品久久久久久中文字幕一区| 精品视频一区二区三区四区五区 | 亚洲福利一区二区三区| 亚洲福利精品一区二区三区| 成人精品一区二区三区不卡免费看 | 亚洲日韩精品一区二区三区无码 | 真实国产乱子伦精品一区二区三区| 人妻精品无码一区二区三区 | 国产福利一区二区在线视频 | 99国产精品欧美一区二区三区| 国产AV天堂无码一区二区三区| 中字幕一区二区三区乱码| 久久精品国产一区二区三区| 日韩福利视频一区| 国产成人av一区二区三区在线| 亚洲AV日韩AV一区二区三曲| 亚洲一区二区在线视频| 97精品国产福利一区二区三区|