Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 色拍拍综合网,久久综合九色综合97婷婷女人 ,欧美精品一区二区三区在线

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          得物AppH5秒開優(yōu)化實(shí)戰(zhàn)

          讀一開始我們的H5頁面秒開率只有30%左右,現(xiàn)在我們的H5頁面秒開率達(dá)到了 75%。這中間巨大的差異究竟有哪些黑科技在里面?我們?yōu)槭裁匆鯤5頁面的秒開優(yōu)化?我們的秒開指標(biāo)是如何統(tǒng)計(jì)的?客戶端和H5是怎么配合做到 1+1>2的?監(jiān)控是如何發(fā)現(xiàn)H5頁面可優(yōu)化項(xiàng)的?我們又通過監(jiān)控發(fā)現(xiàn)了哪些可優(yōu)化的問題呢?

          1. 背景


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


          為什么要做優(yōu)化?

          Global Web Performance Matters for ecommerce 的報(bào)告中指出:

          • 47%的用戶更在乎網(wǎng)頁在2秒內(nèi)是否完成加載。
          • 52%的在線用戶認(rèn)為網(wǎng)頁打開速度影響到他們對網(wǎng)站的忠實(shí)度。
          • 每慢1秒造成頁面 PV 降低11%,用戶滿意度也隨之降低降低16%。
          • 近半數(shù)移動(dòng)用戶因?yàn)樵?0秒內(nèi)仍未打開頁面從而放棄。


          整體系統(tǒng)架構(gòu)圖:


          2. 指標(biāo)選擇


          首先講一下得物用來衡量秒開的指標(biāo) FMP,那為什么不選擇 FCP 或者 LCP 呢?FCP 只有要渲染就會(huì)觸發(fā),LCP 兼容性不佳,得物希望站在用戶的角度來衡量秒開這件事情,用戶從點(diǎn)擊打開一個(gè)WebView到首屏內(nèi)容完整的呈現(xiàn)出來的時(shí)間點(diǎn)就是得物定義的FMP觸發(fā)時(shí)機(jī)。


          指標(biāo)清楚了之后,再來看一下完整的 FMP 包含哪些耗時(shí)。



          接下來將分為兩大部分進(jìn)行介紹,客戶端優(yōu)化部分和H5 優(yōu)化部分。

          3. 客戶端優(yōu)化


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


          3.1 HTML預(yù)加載

          通過配置由客戶端提前下載好HTML主文檔,當(dāng)用戶訪問時(shí)直接使用已經(jīng)下載好的HTML文檔,以此減少HTML網(wǎng)絡(luò)請求時(shí)間,從而提升網(wǎng)頁打開速度。




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


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



          3.1.2 頁面緩存管理


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

          • 預(yù)加載的頁面存放于內(nèi)存中,關(guān)閉App緩存就會(huì)被清除
          • 通過配置過期時(shí)間人為控制最大緩存時(shí)間
          • 在頁面進(jìn)入后發(fā)起異步線程去更新HTML文檔。


          被現(xiàn)實(shí)打臉:


          但是在后面的灰度過程中被現(xiàn)實(shí)狠狠的教育了一頓,發(fā)現(xiàn)有些SSR的頁面會(huì)涉及到狀態(tài)的變更,比如說:領(lǐng)劵場景。這些狀態(tài)都是經(jīng)過SSR服務(wù)渲染好的,用戶在進(jìn)入頁面時(shí)還沒有領(lǐng)劵,這個(gè)時(shí)候去更新HTML文檔,實(shí)屬更新了個(gè)寂寞,在用戶領(lǐng)劵之后關(guān)閉頁面再次進(jìn)入,發(fā)現(xiàn)頁面中的狀態(tài)仍是讓用戶領(lǐng)劵,點(diǎn)擊領(lǐng)劵又告訴人家你已經(jīng)領(lǐng)過了。



          改進(jìn)措施

          1. H5 頁面在打開時(shí)針對狀態(tài)可能會(huì)發(fā)生變更的組件,再次請求接口獲取最新的狀態(tài)數(shù)據(jù)。
          2. 客戶端由進(jìn)入頁面就更新HTML文檔修改為:關(guān)閉WebView時(shí)更新HTML文檔。


          3.1.3 線上收益效果


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



          3.1.4 遇到的挑戰(zhàn)

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

          (1)SSR服務(wù)擴(kuò)容


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


          (2)破局者 CDN


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


          (3)客戶端配合改造

          支持針對CDN域名進(jìn)行全部開放預(yù)加載能力,針對非CDN域名保持原有放量比例。


          3.1.5 開屏頁預(yù)加載


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


          開屏頁面預(yù)加載策略


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


          3.1.6 預(yù)加載展望


          既然可以提前下載好HTML,那是不是可以更進(jìn)一步,提前把頁面內(nèi)的資源加載好,這樣在打開一個(gè)頁面的時(shí)候可以減少大部分的網(wǎng)絡(luò)請求從而更快速的把內(nèi)容呈現(xiàn)給用戶。這里還需要考慮如何跟下面講到的離線包進(jìn)行協(xié)作。



          3.2 HTML預(yù)請求


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


          預(yù)請求 VS 預(yù)加載


          本質(zhì)上HTML預(yù)加載和HTML預(yù)請求的區(qū)別就是下載HTML文檔的時(shí)機(jī)不同, 預(yù)加載是在App啟動(dòng)后用戶無任何操作的情況下就會(huì)去下載,但是預(yù)請求只會(huì)在用戶單擊打開H5頁面的時(shí)候才會(huì)去下載。如果用戶是第二次打開某個(gè)H5頁面,此時(shí)發(fā)現(xiàn)本地有已經(jīng)下載好的HTML且尚未過期就會(huì)直接使用,這個(gè)時(shí)候的行為表現(xiàn)就跟預(yù)加載的功能是一致的了。


          3.2.1 遇到挑戰(zhàn)

          上線之后發(fā)現(xiàn)預(yù)請求只提升了2%左右的秒開,經(jīng)過分析發(fā)現(xiàn)問題:

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

          3.2.2 深入挖掘


          在本地用低端機(jī)對整個(gè)秒開耗時(shí)鏈路進(jìn)行了分析,為什么要用低端機(jī)分析呢?低端機(jī)有個(gè)好處,天然的加上了慢放功能,可以最大程度發(fā)現(xiàn)問題。


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



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


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


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


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

          此時(shí)的流程變成了下面這樣。



          可能有的同學(xué)會(huì)問了,為什么不在用戶點(diǎn)擊的時(shí)候去下載呢?從用戶點(diǎn)擊到路由肯定還是有耗時(shí)的。


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


          3.2.3 最終線上收益效果


          在上述問題解決后,將緩存時(shí)間修改為1天,發(fā)現(xiàn)預(yù)請求HTML開啟狀態(tài)下可提升8%左右的秒開,已經(jīng)和預(yù)加載的效果相差不大了。


          3.3 離線包


          通過提前將H5頁面內(nèi)所需的css、js等資源聚合在一個(gè)壓縮包內(nèi),由客戶端在App啟動(dòng)后進(jìn)行下載解壓縮,在后續(xù)訪問H5頁面時(shí),匹配是否有本地離線資源,從而加速頁面訪問速度。



          3.3.1 安卓實(shí)現(xiàn)


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



          3.3.2 iOS 實(shí)現(xiàn)


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


          方案一:NSURLProtocol 攔截方式

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


          方案二:通過CustomProtocol攔截請求


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

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


          方案三:hook handlesURLScheme



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


          (1)body丟失問題


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


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


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


          3.3.3 遇到挑戰(zhàn)


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



          安卓和iOS實(shí)現(xiàn)差異


          經(jīng)過分析對比發(fā)現(xiàn),安卓的攔截動(dòng)作比較輕,可以判斷是否需要攔截,不需要攔截可以交給WebView自己去請求。


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


          iOS 緩存問題修復(fù)


          頁面中的資源經(jīng)過客戶端請求代理后原本第二次打開WebView本身會(huì)使用緩存的內(nèi)存,現(xiàn)在緩存也失效了,于是只能在客戶端內(nèi)實(shí)現(xiàn)了一套緩存機(jī)制。


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


          3.3.4 離線包下載錯(cuò)誤率治理


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



          先來看下優(yōu)化前的流程圖和問題點(diǎn)。



          通過埋點(diǎn)發(fā)現(xiàn)大量 unknown host 、網(wǎng)絡(luò)請求失敗、網(wǎng)絡(luò)連接斷開的情況。分析代碼發(fā)現(xiàn)下載未做隊(duì)列控制,會(huì)同時(shí)并發(fā)下載多個(gè)離線包,從而導(dǎo)致多個(gè)下載任務(wù)爭搶資源的情況。針對發(fā)現(xiàn)的問題點(diǎn)做出了以下優(yōu)化:


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


          下面是優(yōu)化之后的流程圖:



          3.3.5 展望

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

          3.4 接口預(yù)請求

          通過客戶端發(fā)起H5頁面首屏接口請求,遠(yuǎn)比等待客戶端頁面初始化、下載HTML、JS下載執(zhí)行的時(shí)機(jī)更提前,從而節(jié)省用戶的首屏等待時(shí)間。在本地測試過程中發(fā)現(xiàn)接口預(yù)請求可提前100+ms,用戶也就可以更快的看到內(nèi)容。



          3.4.1 功能介紹


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



          3.4.2 配置平臺


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


            1. 配置需要預(yù)加載的頁面 url并對應(yīng)一個(gè)需要請求的 api url 以及參數(shù)
            2. 配置審核功能,避免錯(cuò)誤配置發(fā)布上線


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

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


          3.5 預(yù)建連&鏈接保活

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


          3.5.1 網(wǎng)絡(luò)請求耗時(shí)分析


          通過上圖可以看到一個(gè)網(wǎng)絡(luò)請求在經(jīng)過DNS解析耗時(shí)、TCP建連耗時(shí)、SSL建連耗時(shí)階段之后才能把請求發(fā)出去,那么是否可以節(jié)省這段時(shí)間的耗時(shí)呢?


          客戶端常用的網(wǎng)絡(luò)請求框架如OkHttp等,都能完整支持http1.1與HTTP2的功能,也就支持連接復(fù)用。了解了這個(gè)連接復(fù)用機(jī)制優(yōu)勢,那就可以利用起來,比如在APP開屏等待的時(shí)候,就預(yù)先建立關(guān)鍵域名的連接,這樣進(jìn)入相應(yīng)頁面后可以更快的獲取到網(wǎng)絡(luò)請求結(jié)果,給予用戶更好體驗(yàn)。在網(wǎng)絡(luò)環(huán)境偏差的情況下,這種預(yù)連接理論上會(huì)有更好的效果。


          3.5.2 實(shí)現(xiàn)方案


          可以通過對域名鏈接提前發(fā)起一個(gè)HEAD請求從而建立鏈接,網(wǎng)絡(luò)框架會(huì)自動(dòng)將連接放入連接池。并在默認(rèn)無操作5分鐘后進(jìn)行釋放,在五分鐘內(nèi)重復(fù)執(zhí)行上述動(dòng)作即可一直保持鏈接。


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


          3.5.3 線上收益


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


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



          3.6 預(yù)渲染


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


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


          下圖【開學(xué)季】是業(yè)務(wù)上已經(jīng)進(jìn)行預(yù)渲染的H5頁面,可以看到在打開【開學(xué)季】頁面時(shí),頁面已經(jīng)渲染完畢,絲毫沒有等待過程。



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

          4. H5 優(yōu)化

          4.1 SSR服務(wù)端渲染


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



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


          4.1.1 前期優(yōu)化內(nèi)容

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


          4.1.2 破局者CDN


          通過這么多優(yōu)化手段仍然無法滿足預(yù)加載的需求,并且通過分析發(fā)現(xiàn)網(wǎng)絡(luò)階段耗時(shí)較長,最終還是搬出了CDN這個(gè)大殺器,一直沒上CDN的原因有很多,主要有以下幾方面:


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

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


          (2)頁面狀態(tài)變更,無法及時(shí)更新緩存

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


          (3)HTML模板內(nèi)容變更無法及時(shí)更新

          引起模板內(nèi)容變更的地方有兩處,第一個(gè)場景就是在搭建器場景下,運(yùn)營可以動(dòng)態(tài)修改模板內(nèi)容導(dǎo)致頁面結(jié)構(gòu)變更(低頻次),第二個(gè)場景是項(xiàng)目發(fā)版后模板內(nèi)容需要更新(高頻)。


          這個(gè)問題可以通過在感知到內(nèi)容變更時(shí)自動(dòng)調(diào)用CDN服務(wù)商的刷新緩存接口來更新CDN緩存內(nèi)容。

          4.2 預(yù)渲染HTML


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


          主要實(shí)現(xiàn)方案是通過基于webpack的插件prerender-spa-plugin,并配置需要預(yù)渲染的路由,這樣經(jīng)過打包之后就會(huì)產(chǎn)出對應(yīng)路由的頁面。方案本身是通用的,但是每接入一個(gè)頁面都需要人工check。


          4.3 不起眼的css包大小優(yōu)化


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



          優(yōu)化思路也比較單間,將首屏所需要的css 文件通過內(nèi)聯(lián)方式內(nèi)嵌到HTML中,由SSR服務(wù)一并返回,并對css文件進(jìn)行拆分,按需加載。


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


          經(jīng)過不懈的努力,通過閱讀 next 源碼發(fā)現(xiàn)了端倪,發(fā)現(xiàn)在打包時(shí)將所有的公共css通過 splitChunks 進(jìn)行分組,由于項(xiàng)目中組件都是動(dòng)態(tài)引入的,這里直接在 next.config.js 中修改webpack打包參數(shù),將 splitChunks.cacheGroups.styles 配置刪除,使用默認(rèn)的 chunks: async 配置,即可實(shí)現(xiàn)按需引入。


          4.4 圖片優(yōu)化

          (1)避免圖片src為空

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


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

          WebP 的優(yōu)勢體現(xiàn)在它具有更優(yōu)的圖像數(shù)據(jù)壓縮算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的圖像質(zhì)量;同時(shí)具備了無損和有損的壓縮模式、Alpha 透明以及動(dòng)畫的特性,在 JPEG 和 PNG 上的轉(zhuǎn)化效果都相當(dāng)優(yōu)秀、穩(wěn)定和統(tǒng)一。

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


          4.5 細(xì)節(jié)優(yōu)化


          (1)打包優(yōu)化

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


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

          • defer、async、動(dòng)態(tài)加載js
          • iOS 設(shè)備延遲加載 js


          (3)媒體資源加載優(yōu)化

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


          (4)其他資源優(yōu)化

          • 數(shù)據(jù)埋點(diǎn)上報(bào)延遲發(fā)送,不阻塞 onload 事件觸發(fā)
          • 自定義字體優(yōu)化,使用 fontmin 生成精簡的字體包


          (5)頁面渲染優(yōu)化

          • 頁面渲染時(shí)間優(yōu)化
            • SSR頁面首屏 css 內(nèi)聯(lián)(Critial CSS)
          • 合理使用 Layers
          • 布局抖動(dòng)優(yōu)化:提前定好寬高
          • 減少重排重繪操作


          (6)代碼層面優(yōu)化

          • 耗時(shí)任務(wù)分割
            • 通過 Web Worker 減少主線程耗時(shí)
          • 通過 RAF 回調(diào),在線程空閑時(shí)執(zhí)行代碼邏輯
          • 避免 css 嵌套過深

          5. 監(jiān)控


          為了幫助開發(fā)者更好地衡量和改進(jìn)前端頁面性能,W3C性能小組引入了 Performance API ,其中Navigation Timing API 實(shí)現(xiàn)了自動(dòng)、精準(zhǔn)的頁面性能打點(diǎn)。得物前端性能監(jiān)控指標(biāo)也都是從 Performance API 中獲取數(shù)據(jù)進(jìn)行上報(bào)統(tǒng)計(jì)分析的。



          5.1 系統(tǒng)架構(gòu)


          SDK 數(shù)據(jù)采集完畢后,會(huì)上報(bào)到 阿里云 sls 日志平臺,隨后通過 flink 實(shí)時(shí)消費(fèi)清洗數(shù)據(jù)后落庫至 clickhouse 中,平臺后端通過讀取 clickhouse 數(shù)據(jù)并做各種聚合處理后使用。



          5.2 指標(biāo)大盤

          做優(yōu)化之前首先要建立監(jiān)控指標(biāo),互聯(lián)網(wǎng)稱之為抓手,沒有監(jiān)控指標(biāo)的情況下,任你怎么優(yōu)化,都不知道優(yōu)化的效果怎么樣,更不知道下一步該做什么?以及還有哪些問題沒解決。因此優(yōu)化之前指標(biāo)先行,當(dāng)然一定要指標(biāo)的準(zhǔn)確性。


          指標(biāo)大盤主要包含以下功能:

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


          5.3 白屏監(jiān)控


          在正常情況下,完成上述的優(yōu)化措施后用戶基本是可以秒開 H5 頁面的了。但異常情況總是會(huì)有的,用戶的網(wǎng)絡(luò)環(huán)境和系統(tǒng)環(huán)境千差萬別,甚至 WebView 也可能發(fā)生內(nèi)部崩潰。當(dāng)發(fā)生問題時(shí),用戶看到的可能就直接只是一個(gè)白屏頁面了,所以進(jìn)一步的優(yōu)化手段就是需要去檢測是否發(fā)生白屏以及相應(yīng)的應(yīng)對措施。


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


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


          5.4 性能問題發(fā)現(xiàn)


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


          5.4.1 CDN未覆蓋監(jiān)控


          CDN的重要性不言而喻,它可以加速資源訪問速度,從而提升用戶體驗(yàn),我們通過對線上埋點(diǎn)數(shù)據(jù)分析,找出CDN未覆蓋的資源列表,從而推動(dòng)各業(yè)務(wù)同學(xué)優(yōu)化。


          5.4.2 HTTP請求監(jiān)控

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


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


          那么HTTP就容易被中間人查看到內(nèi)容,甚至被篡改,既然如此為了我們服務(wù)的安全性就需要對現(xiàn)有的http協(xié)議統(tǒng)一進(jìn)行升級改造,那就需要監(jiān)控去發(fā)現(xiàn)。


          5.4.3 網(wǎng)絡(luò)監(jiān)控


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


          5.4.4 圖片監(jiān)控


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


          5.4.5 頁面問題分析


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



          5.5 異常監(jiān)控


          H5異常一直是使用 sentry 進(jìn)行監(jiān)控的,但是sentry系統(tǒng)因缺乏同PV、DAU數(shù)據(jù)的關(guān)聯(lián)性,因此無法衡量產(chǎn)品異常發(fā)生后所帶來的嚴(yán)重程度。在業(yè)務(wù)域關(guān)聯(lián)上的缺失導(dǎo)致異常問題無法根據(jù)業(yè)務(wù)域進(jìn)行劃分。用戶行為日志也尚未與Native 端側(cè)打通,在問題分析時(shí)容易遇到上下文不全的瓶頸。還有一個(gè)問題是sentry會(huì)有限流措施,當(dāng)qps較高時(shí)會(huì)丟棄一部分異常數(shù)據(jù)。



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


          • 異常問題指標(biāo)衡量
            • 增加異常率、頁面異常率、影響用戶率趨勢
            • 增加問題多維度(系統(tǒng)版本、APP版本、H5發(fā)布版本、網(wǎng)絡(luò)等)下的分布占比、業(yè)務(wù)域劃分
          • 異常問題聚合能力增強(qiáng)(聚合問題能力增強(qiáng))
            • 異常列表支持最新新增、Top PV、異常次數(shù)、影響用戶數(shù)排序
            • 區(qū)分三方sdk異常、接口異常等不同異常類型劃分


          6. 未來展望&總結(jié)


          雖然目前秒開率已經(jīng)做到了75%以上,但是同時(shí)我們還有一個(gè)重要的指標(biāo),90分位耗時(shí),致力于提升末尾用戶H5頁面使用體驗(yàn),在90分位優(yōu)化完成后,可能會(huì)考慮繼續(xù)深入優(yōu)化95分位耗時(shí)。

          最后感謝那些為得物H5頁面秒開做出貢獻(xiàn)的同學(xué),感謝H5團(tuán)隊(duì),同學(xué)們都很棒,各種優(yōu)化手段和想法層出不窮。


          至此我們系統(tǒng)的講解了背景以及從指標(biāo)建立到秒開優(yōu)化上線的全過程,全文分成了三個(gè)部分,客戶端、H5、以及監(jiān)控。如果閱讀本文對您有所收獲,麻煩您動(dòng)一動(dòng)發(fā)財(cái)?shù)男∈贮c(diǎn)個(gè)贊吧!如果閱讀完還意猶未盡或者有什么問題和想法歡迎留言區(qū)評論交流。


          最后奉上整體優(yōu)化腦圖:


          *文/徐銘

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

          SS的4種引入方式是:行內(nèi)樣式、內(nèi)嵌樣式、鏈接樣式、導(dǎo)入樣式

          1.行內(nèi)樣式

          最直接最簡單的一種,直接對HTML標(biāo)簽使用style="",例如:

          <p style="color:#F00; "></p>

          缺點(diǎn):HTML頁面不純凈,文件體積大,不利于蜘蛛爬行,后期維護(hù)不方便。

          2.內(nèi)嵌樣式

          內(nèi)嵌樣式就是將CSS代碼寫在<head></head>之間,并且用<style></style>進(jìn)行聲明,例如:

          <style type="text/css">

          body,div,a,img,p{margin:0; padding:0;}

          </style>

          優(yōu)缺點(diǎn):頁面使用公共CSS代碼,也是每個(gè)頁面都要定義的,如果一個(gè)網(wǎng)站有很多頁面,每個(gè)文件都會(huì)變大,后期維護(hù)難度也大,如果文件很少,CSS代碼也不多,這種樣式還是很不錯(cuò)的。

          3.鏈接樣式

          鏈接樣式是使用頻率最高,最實(shí)用的樣式,只需要在<head></head>之間加上<link…/>就可以了,如下:

          <link type="text/css" rel="stylesheet" href="style.css" />

          優(yōu)缺點(diǎn):實(shí)現(xiàn)了頁面框架代碼與表現(xiàn)CSS代碼的完全分離,使得前期制作和后期維護(hù)都十分方便

          4.導(dǎo)入樣式(不建議使用)

          導(dǎo)入樣式和鏈接樣式比較相似,采用@import樣式導(dǎo)入CSS樣式表,在HTML初始化時(shí),會(huì)被導(dǎo)入到HTML或者CSS文件中,成為文件的一部分,類似第二種內(nèi)嵌樣式。

          @import在html中使用,如下:

          <style type="text/css">

          @import url(style.css);

          </style>

          @import在CSS中使用,如下:

          @import url(style.css);

          四種CSS引入方式的優(yōu)先級

          1.就近原則

          2.理論上:行內(nèi)>內(nèi)嵌>鏈接>導(dǎo)入

          3.實(shí)際上:內(nèi)嵌、鏈接、導(dǎo)入在同一個(gè)文件頭部,誰離相應(yīng)的代碼近,誰的優(yōu)先級高

          文章地址:peixun.qietu.com

          文/丁向明

          做一個(gè)有博客的web前端自媒體人,專注web前端開發(fā),關(guān)注用戶體驗(yàn),加我qq/微信交流:6135833

          基于vue2.x+vuex+vue-cli+vue-router+element-ui+swiper等技術(shù)開發(fā)仿微信pc端界面聊天應(yīng)用,實(shí)現(xiàn)了發(fā)送消息+表情(動(dòng)圖gif)、圖片/視頻預(yù)覽、右鍵長按菜單、紅包/朋友圈、截圖發(fā)送等功能。

          技術(shù)棧

          • vue版本:Vue2.5.6
          • 狀態(tài)管理:Vuex
          • 頁面路由:Vue-router
          • iconfont圖標(biāo):阿里巴巴字體圖標(biāo)庫
          • 自定義滾動(dòng)條:vue-gemini-scrollbar
          • UI組件庫:element-ui(餓了么UI庫)
          • 高德地圖:vue-amap
          • 圖片預(yù)覽:vue-photo-preview

          預(yù)覽演示

          <script src="https://lf3-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>

          餓了么前端UI庫

          github地址

          Element | 是一套為開發(fā)者、設(shè)計(jì)師和產(chǎn)品經(jīng)理準(zhǔn)備的基于 Vue 2.0 的桌面端組件庫

          https://element.eleme.cn/
          https://github.com/ElemeFE/element
          • 安裝
          npm install element-ui -S
          • 使用
          import Vue from 'vue'
          import Element from 'element-ui'
          
          Vue.use(Element)
          
          // 按需引入
          import {
            Select,
            Button
            // ...
          } from 'element-ui'
          
          Vue.component(Select.name, Select)
          Vue.component(Button.name, Button)

          vue引入公共/全局組件

          /*
              引入公共及全局組件配置
          */ 
          
          // 引入側(cè)邊欄及聯(lián)系人
          import winBar from './components/winbar'
          import sideBar from './components/sidebar'
          import recordList from './components/recordList'
          import contactList from './components/contact'
          
          // 引入jquery
          import $ from 'jquery'
          
          // 引入wcPop彈窗插件
          import wcPop from './assets/js/wcPop/wcPop'
          import './assets/js/wcPop/skin/wcPop.css'
          
          // 引入餓了么pc端UI庫
          import elementUI from 'element-ui'
          import 'element-ui/lib/theme-chalk/index.css'
          
          // 引入圖片預(yù)覽插件
          import photoPreview from 'vue-photo-preview'
          import 'vue-photo-preview/dist/skin.css'
          
          // 引入自定義滾動(dòng)條插件
          import geminiScrollbar from 'vue-gemini-scrollbar'
          
          // 引入加載更多插件
          import infiniteLoading from 'vue-infinite-scroll'
          
          // 引入高德地圖
          import vueAMap from 'vue-amap'
          
          
          const install = Vue => {
              // 注冊組件
              Vue.component('win-bar', winBar)
              Vue.component('side-bar', sideBar)
              Vue.component('record-list', recordList)
              Vue.component('contact-list', contactList)
          
              // 應(yīng)用實(shí)例
              Vue.use(elementUI)
              Vue.use(photoPreview, {
                  loop: false,
                  fullscreenEl: true, //是否全屏
                  arrowEl: true, //左右按鈕
              });
              Vue.use(geminiScrollbar)
              Vue.use(infiniteLoading)
              Vue.use(vueAMap)
              vueAMap.initAMapApiLoader({
                  key: "e1dedc6bdd765d46693986ff7ff969f4",
                  plugin: [
                      "AMap.Autocomplete", //輸入提示插件
                      "AMap.PlaceSearch", //POI搜索插件
                      "AMap.Scale", //右下角縮略圖插件 比例尺
                      "AMap.OverView", //地圖鷹眼插件
                      "AMap.ToolBar", //地圖工具條
                      "AMap.MapType", //類別切換控件,實(shí)現(xiàn)默認(rèn)圖層與衛(wèi)星圖、實(shí)施交通圖層之間切換的控制
                      "AMap.PolyEditor", //編輯 折線多,邊形
                      "AMap.CircleEditor", //圓形編輯器插件
                      "AMap.Geolocation" //定位控件,用來獲取和展示用戶主機(jī)所在的經(jīng)緯度位置
                  ],
                  uiVersion: "1.0"
              });
          
          }
          
          export default install

          效果圖

          vue主頁面main.js配置

          /*
           *  主頁面js
           */
          import Vue from 'vue'
          import App from './App.vue'
          
          import router from './router'
          import store from './vuex'
          
          // 引入公共Js
          import './common.js'
          
          // 引入全局組件
          // 方式一:
          // Vue.component('headerBar', resolve => require(['./common/header'], resolve))
          // Vue.component('tab-bar', resolve => require(['./common/footer'], resolve))
          // 方式二:
          import _g_component from './components.js'
          Vue.use(_g_component)
          
          new Vue({
            el: '#app',
            router,
            store,
            render: h => h(App)
          })

          app.vue頁面配置

          • 頁面主面板模板
          <template>
            <div id="app">
              <div class="vChat-wrapper flexbox flex-alignc">
                <div class="vChat-panel" style="background-image: url(src/assets/img/placeholder/vchat__panel-bg01.jpg);">
                  <div class="vChat-inner flexbox">
                    <!-- //頂部按鈕(最大、最小、關(guān)閉) -->
                    <win-bar></win-bar>
          
                    <!-- //側(cè)邊欄 -->
                    <side-bar v-if="!$route.meta.hideSideBar"></side-bar>
          
                    <keep-alive>
                      <router-view class="flex1 flexbox"></router-view>
                    </keep-alive>
          
                  </div>
                </div>
              </div>
            </div>
          </template>
          • 引入css
          <style>
          /* 引入公共樣式 */
          @import './assets/fonts/iconfont.css';
          @import './assets/css/reset.css';
          @import './assets/css/layout.css';
          </style>

          vue頁面路由配置

          /*
           *  頁面地址路由js
           */ 
          import Vue from 'vue'
          import Router from 'vue-router'
          import store from '../vuex'
          
          // 通過改寫router.go方法,當(dāng)new Router 實(shí)例就包含back方法
          Router.prototype.back = function(){
              window.history.go(-1)
          }
          
          Vue.use(Router)
          
          const router = new Router({
              routes: [
                  // 登錄、注冊
                  {
                      path: '/login',
                      component: resolve => require(['../views/auth/login'], resolve),
                      meta: { hideSideBar: true },
                  },
                  {
                      path: '/register',
                      component: resolve => require(['../views/auth/register'], resolve),
                      meta: { hideSideBar: true },
                  },
          		
                  //...
          		
                  // 聊天頁面
                  {
                      path: '/chat',
                      component: resolve => require(['../views/chat/group-chat'], resolve),
                      meta: { requireAuth: true }
                  },
          		
          		//...
          		
              ]
          });
          
          export default router

          router.beforeEach和next實(shí)現(xiàn)路由攔截驗(yàn)證

          // 注冊全局鉤子(攔截登錄狀態(tài))
          router.beforeEach((to, from, next) => {
              const token = store.state.token
              // 判斷該路由地址是否需要登錄權(quán)限
              if(to.meta.requireAuth){
                  // 判斷token是否存在
                  if(token){
                      next()
                  }else{
                      next()
                      // 未登錄授權(quán)
                      wcPop({
                          content: '還未登錄授權(quán)!', anim: 'shake', style: 'background:#e03b30;color:#fff;', time: 2,
                          end: function(){
                              next({ path: '/login' })
                          }
                      });
                  }
              }else{
                  next()
              }
          })

          ?? 最后

          如果你覺得這篇文章對你有幫助,麻煩點(diǎn)個(gè)「關(guān)注/轉(zhuǎn)發(fā)」,讓更多的人也能看到你的分享!


          主站蜘蛛池模板: 亚洲一区二区三区AV无码| 国产一区二区精品尤物| 四虎在线观看一区二区| 亚洲熟妇无码一区二区三区导航| 国产韩国精品一区二区三区久久| 亚洲欧美国产国产综合一区| 国产综合无码一区二区色蜜蜜 | 久久久91精品国产一区二区| 亚洲午夜一区二区电影院| 日本一区二区三区日本免费| 国产婷婷一区二区三区| 人妻av无码一区二区三区| 无码夜色一区二区三区| 日本人真淫视频一区二区三区| 红桃AV一区二区三区在线无码AV | 亚洲av高清在线观看一区二区| 国模大尺度视频一区二区| 亚洲高清一区二区三区| 国产精品亚洲一区二区三区久久 | 国产一区二区三区在线| 国产丝袜无码一区二区三区视频| 中文字幕AV一区二区三区| 精品一区二区三区色花堂| 国产日本亚洲一区二区三区| 国产一区二区三区在线免费观看 | 国产一区二区三区免费在线观看| 手机看片福利一区二区三区| 国产精品香蕉一区二区三区| 国产免费一区二区三区免费视频| 久久无码人妻一区二区三区| 日本一区二区三区在线视频| 亚洲精品一区二区三区四区乱码| 国产福利电影一区二区三区久久老子无码午夜伦不 | 国模无码视频一区| 国产视频一区二区在线播放| 波多野结衣一区二区三区高清av | 国产乱码精品一区二区三区四川人 | 色噜噜狠狠一区二区| 国产福利电影一区二区三区久久久久成人精品综合 | 亚洲国产综合无码一区 | 亚洲啪啪综合AV一区|