基于VUE的SPA單頁應用開發-加載性能篇

1、基于異步數據的vue頁面刷新

先看看基于異步數據的vue頁面刷新后,都發生了啥~

如圖所示:

圖1 基于異步數據的vue頁面刷新 網絡請求圖

步驟如下:

step1:請求頁面;

step2:請求頁面內的css、js資源;

step3:vue頁面初始化;

step4:頁面渲染,框架呈現[無數據內容];

step5:請求頁面實際數據;

step6:數據ready,填充視圖,圖片資源加載;

step7:完整頁面呈現。

步驟分析:

step1:請求html文件;

step2:請求資源;

優化點:

a、多次訪問的資源緩存:可從MD5、組件打包方式等角度再細分;

b、app框架資源預加載:如果是hybird開發的app,可通過app框架預加載的方式,將單頁應用的資源提前緩存。

單頁的css、js資源,與傳統頁面的資源相比,規模要大很多。其集合了幾乎單頁應用的所有css、js文件,隨著應用的規模大小成正比增長。合理的緩存處理,將大大提升頁面加載速度。a、b兩點可實現性能加速的原因是,本地加載過的資源,會緩存在本地;頁面請求資源時,瀏覽器會先查找緩存,如果有緩存,則本地取,節省了網絡請求。[可了解瀏覽器的強緩、弱緩]

step3:頁面初始化;

優化點:

利用v-if指令按需加載組件~

由于vue在初始化過程中,會深度查找子組件,生成依賴,構建虛擬DOM,所以其初始化時間相對較長;不過在查找過程中,遇到v-if為false的組件,將停止深度查找,從而節省初始化時間。由此可通過控制v-if的布爾值,實現性能優化和組件的按需加載。例如在初始化過程中,僅首屏必須的組件v-if=true,其他組件[如非首屏組件、彈窗組件等],可在下一事件循環中[通過setTimeout(fn, 0)等異步操作可實現],或者首屏資源加載完成后,開啟。

step4:默認數據頁面渲染;(默認數據由vuex提供)

優化點:無數據的頁面框架的渲染展現[通常所說的灰框],讓用戶提前感知頁面,從而提升用戶體驗;(包括讀取vuex數據,進行渲染)

step5:異步請求數據,與step4同時進行,通過ajax實現;

step6:資源加載;

優化點:進行圖片[視頻]分批加載優化,從而增加同一帶寬下單圖的加載速度,加速首屏展現。

建議圖片加載流程:

step6-1:首屏展現必須圖片加載及首屏默認圖片;[如:banner圖第一張];

step6-2:首屏其余圖片加載及其他默認圖片;[如:剩余的banner圖];

step6-3:非首屏 or 彈窗。[接下來的加載順序,可根據需求調整。但要遵循一條原則,不影響首屏的用戶交互。]

2、基于異步數據的vue頁面的路由跳轉

再來看看通過單頁路由跳轉到新頁,又發生了什么?

圖2 基于異步數據的vue頁面路由跳轉 網絡請求圖

頁面加載步驟:

step1:捕獲到路由變化;

step2:初始化該頁面,并默認數據渲染;(包括讀取vuex數據,進行渲染)[step3、step4]

step3:異步請求最新的初始化數據;[同step5]

step4:資源分批預加載。[同step6]

總結:

優勢:頁內跳轉性能非常贊。對比圖1和圖2,在路由內跳轉時減少了圖1中step1、step2的頁面請求和.css、.js的請求時間[節省1s+],頁面展現嗖嗖的。再好好結合vuex的數據流,可以給用戶非常棒的體驗。

劣勢:再觀察圖1的網絡請求圖,可以發現以下幾點:

a、css、js資源相對傳統頁面,量更大,加載時間加長;

b、vue的首屏展現,依賴異步數據的請求,相對傳統同步頁面,增加了單獨的數據請求時間消耗;

c、頁面渲染,在js執行完畢之后,才開始進行;而最終的首屏展現,則需要等待異步數據請求到達之后。

由于a、b的存在,c的首屏展現時間相對傳統頁面更慢。

VUE的異步單頁應用優勢與劣勢非常明顯,缺點是初始化時間長,依賴js資源的加載;優勢是運行速度快,路由內跳轉幾乎沒太多的時間消耗。如果是一定規模大小的單頁應用,它將是不錯的選擇。特別是使用hybird開發,通過app框架將資源預加載之后,需依賴js資源的劣勢也必將不存在,那將給到用戶傳統頁面無法給到的體驗。

Q:那有什么辦法來解決這些劣勢嗎?

A:在接下來的3中,將提出一種解決方案。

3、提速方案

對于a點,資源量大,可以從打包方式、緩存、CDN分發等角度進行處理;

對于b點,有兩種方式解決:

1、同步+異步數據請求:刷新頁面時,使用同步MVC框架的方式,通過后臺路由帶入初始化數據;頁內路由跳轉時,仍然采用異步的方式進行。

2、異步數據請求提前:刷新頁面時,將數據請求提前至js資源加載前,由于網絡請求可并發多個,將節省單獨的數據請求時間。

tips:如果不是mvvm的異步單頁,推薦使用同步+異步的方式,頁面的展現可以提前至js資源加載之前。[由于mvvm框架下的頁面視圖通過數據進行驅動,該驅動的基本需要依賴js腳本實現,所以必須等待js加載完畢,才能正確展現頁面。因此,在mvvm框架下,同步+異步的方式僅能節省數據請求時間,但其他單頁應用可以節省數據請求時間+js資源加載時間]

ps:js的加載順序:不影響頁面初始化呈現的js底部后置:如日志、分享、im的相關js。

4、初始化性能優化[可用于加速首屏呈現]

如下示例:

其頁面結構與組件結構關系圖如下:

vue初始化的組件編譯原則是,按照深度查找,遇到v-if為false的節點或者葉子節點,停止查找。從示例的組件結構圖,我們可以看出,

初始化中組件查找過程為:

step1:首先查找根節點的子組件nav組件,其為葉子節點,編譯,返回;

step2:查找app的第二個子組件(節點)content,其非葉子節點,且無v-if標記,繼續深度查找;查找其子節點c-a組件,為葉子節點,編譯,返回content;查找另一個子節點c-b組件,葉子節點,編譯,返回;

step3:節點content查找完畢;返回app,查找sidebar節點,sidebar非葉子節點,且無v-if,繼續深度查找;同step2,最終返回app節點。

step4:所有組件編譯完畢,初始化完畢,渲染。

如上示例,將深度遍歷所有子組件,再完成渲染。如果將首屏不需要展現的組件設置成v-if,將降低深度查找的復雜度,從而加速組件初始化,加速頁面的呈現。

加速代碼如下:

? ? ? ? ? ? newVue({? ? data: {? ? ? ? showContent:false,? ? ? ? showSidebar:false},? ? created () {//顯示content? ? ? ? setTimeout(()=>{this.showContent =true;? ? ? ? },0);//顯示sidebar? ? ? ? setTimeout(()=>{this.showSidebar =true;? ? ? ? },0);? ? }});

以上代碼將組件content與sidebar的v-if設置成false,組件的編譯查找過程如下:

step1:不變;

step2:查找content組件,其為v-if=false,停止,并返回根節點;

step3:查找sidebar組件,其為v-if=false,停止,并返回根節點;

step4:所有組件編譯完畢,初始化完畢,渲染。

是不是快了很多~~由此,首先被渲染,出現的是nav組件結構;另外兩個組件通過showContent,showSidebar控制。

為何這里使用setTimeout(fn, 0)控制兩個組件的狀態變化呢?

因為setTimeout是時間異步處理模塊,通過其設置,相應的處理方法將在下一事件循環中才被執行,而VUE的渲染時機為本次主線程執行完畢。如此,使得另外兩個組件的編譯推遲到首次渲染之后,從而實現組件加速。

tips:這里需要注意,不是將組件設置v-if=false就可以了,要看看v-if=true的開啟時機,如果是同一事件循環中被開啟,便沒有意義了。因為vue的數據驅動渲染時機,是同一事件循環中的代碼全部執行完畢之后,拿到數據的最終狀態才進行。同時,setTimeout(fn, 0)也不可亂用。

附加:圖片分批預加載的js腳本

參數說明:

auto: 是否自執行

imgs: 需預加載的圖片列表,為二維表

ignore:在自執行過程中,需要跳過的圖片批次腳標

firstSetReady: 第一組圖片完成加載以后,置為true,便于外部掌握狀態[一般首屏資源為第一組圖片]

finished: 所有圖片資源加載完畢

說明:

非自執行的需求,可直接調用loadOneSetImages方法,返回值為promise

letco =require('co')classPreload{// 定義構造函數的數據結構,創建實例對象時,自動初始化constructor(auto, imgs = [], ignore = []) {this.imgs = imgs;this.ignore = ignore;this.auto = auto;this.firstSetReady =false;this.finished =false;this.init();? ? }// 初始化函數init() {letme =this// 如果自動執行,則調用co模塊,自動加載資源if(this.auto) {// generator的自執行函數,資源加載完畢時,參數finished置為trueco(this.autoExeImageStream.call(this)).then(function(){console.log('資源加載完畢~')? ? ? ? ? ? ? ? me.finished =true}).catch(function(){console.log('資源加載出錯~')? ? ? ? ? ? ? ? me.finished =true})? ? ? ? }? ? }// 同步加載分批圖片資源,使用generator函數,完成當前批次加載,再啟動下一批次的加載* autoExeImageStream() {letrstList = []for(leti =0; i

圖片分批預加載腳本通過ES6 promise結合generator函數實現,使得圖片按照我們想要的方式順序加載,且單批次速度更快。但不能濫用,需要留意圖片資源的加載規模與用戶交互操作之間的關系。由于,ES6 的 promise在事件循環中的消息處理級別高于DOM事件[or 網絡請求等其他異步模塊],當有 promise 消息要處理時,其他事件消息將等待,直到promise處理完畢。因此在使用其進行預加載時,一定要結合業務情況及預加載的需求進行設置。如有需要可以創建多個preload實例,通過一定的條件,觸發圖片預加載,將其進行分散,從而達到性能加速的同時,用戶交互體驗也不受影響。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,595評論 25 708
  • 30余年的第一次出去旅行,第一次坐飛機,飛機飛起的那一刻我在想,兒子,你沒坐過飛機,下次媽媽一定帶你...
    傻傻的二丫頭閱讀 211評論 2 1