移動端H5活動頁優化方案

来源:https://www.cnblogs.com/liuyongjia/archive/2018/04/06/8728900.html
-Advertisement-
Play Games

背景 項目 :移動端H5電商項目 痛點 :慢!!! 初始方案 :最基本的圖片懶載入,靜態資源放到cdn,predns等等已經都做了。但是還是慢,慢在哪? 顯而易見的原因 :由於前後端分離,所有的數據都由介面下發,之後根據模板渲染頁面。也就是說,我們需要先載入js,等到js載入完畢之後,請求介面,介面 ...


背景

項目:移動端H5電商項目
痛點:慢!!!
初始方案:最基本的圖片懶載入,靜態資源放到cdn,predns等等已經都做了。但是還是慢,慢在哪?
顯而易見的原因:由於前後端分離,所有的數據都由介面下發,之後根據模板渲染頁面。也就是說,我們需要先載入js,等到js載入完畢之後,請求介面,介面返回數據之後,渲染頁面,載入圖片等等。儘管使用了模塊化的載入方式,但是對於要求高的首頁和活動頁,給用戶的感知也不是很好。

初版解決方案

最初,由於時間緊迫,基本上都是從客戶端作優化處理,基本上可以總結為以下幾個方面。

一、本地緩存

我們做了本地緩存優化的策略,第一次請求之後就把介面數據緩存到localStorage裡面,並且存儲當時的時間,設定過期時間,一般設置為5分鐘,用戶在5分鐘內重覆打開頁面,不會再次請求介面,從localstorage中拿取數據,直接渲染頁面。
後續乾脆把模板渲染好的html片段存儲了起來,直接拼接,省去了模板計算的時間。
基本實現方案如下:

var 
    cache = localStorage.getItem('cache')
    , expires = 5 * 60 * 1000
;

// 判斷是否過期
function isOverdue(pastTime, expires) {
    return Date.now() - pasttime >= expires;
}

if (cache && !isOverdue(cache.time, expires)) {
    // 說明緩存存在,並且沒有過期
    // 就正常取cache.data做相應的渲染
} else {
    // 說明緩存不存在或者已經過期了
    // 重新請求介面
    $.get('a.cn', funciton (res) {
        // do something
        // 把對應的渲染操作處理完成之後,將數據緩存,並記錄當前的時間
        localStorage.setItem('cache', {
            data: res,
            time: Date.now()
        })
    })
} 

然而還是不夠,新用戶在首次打開時,還是不能秒開頁面,並且用戶在5分鐘之後重新載入之時,仍然會有一定的延遲(由於瀏覽器會緩存一部分靜態資源,此時再打開並不會像用戶初次打開一樣那麼慢)。

二、進一步緩存靜態資源

在日常開發中,有很多依賴庫,常用的fastclick,swipe等等,這些庫,沒有必要每次都去載入,雖然瀏覽器會對一些靜態資源做緩存,但是卻不能完全被我們控制,所以,可以將這些不常發生變化的靜態資源緩存起來,同樣的,存到localStorage裡面。

需要註意

這個方案有一個問題,如果是直接載入的script標簽,是無法直接拿到它的腳本內容的。

<script id="script1" src="js/jquery.js"></script>
console.log(document.querySelector('#script1').innerHTML);
// 此時輸出的是undefined,因為innerHTML是獲取標簽內容,此時script標簽里並沒有內容。
<script id="script2">
console.log('2');
</script>
console.log(document.querySelector('#script2').innerHTML);
// 此時輸出的是console.log('2');
// 因為innerHTML是獲取標簽內容,此時script標簽里並沒有內容。

這當然不是我們想要的,我們需要的是外鏈js的可執行代碼。

動態添加js的兩種方案

我在前一篇高性能JavaScript讀書筆記中提到了兩種方案。

1. 動態腳本元素

var script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function () {
    // do something
}
script.src = 'jquery.js';
document.getElementByTagName('heda')[0].appendChild(script);

這種方法可以監控到腳本的完成事件,但是由於也是通過添加一個script標簽,並不能拿到我們想要的js代碼。

2. 通過XMLHttPRequest腳本註入

var xhr = new XMLHttpRequest();
xhr.open("get", "file1.js", true);
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
            localStorage.setItem('file1', xhr.responseText);
        }
    }
}

需要特別註意的是,這個方法有跨域的風險,所以,我們需要靜態資源伺服器的allow-origin設置為*。或者直接載入本功能變數名稱下的js。

有同學要問了,既然localStorage這麼強大,為什麼不把所有的東西都緩存起來呢?
當然是因為它的大小有限制,在FireFox和chrome中,一般來說,sessionStorage和localStorage大小為10MB,而safari只有5MB。詳見這篇文章
這就限制了我們什麼都存的想法。如果圖片較小,可以緩存起來。

第二版解決方案

在前一版方案里,我們解決了後續載入的速度緩慢問題,在後續的頁面打開速度上,基本上可以做到秒開。
但是這個方案還是有不足,對於新用戶的體驗不是很好,如果用戶在5min的這個時間點上打開,速度還是會有所下降。
最後還是只能做SSR。
知乎上有一個問題,為什麼現在又流行服務端渲染html?
服務端渲染有很多好處,對我們此時而言,最大的好處就是頁面直出。省去了請求介面這一步操作。並且對於提高用戶體驗上來說,很有好處。
如果時間充裕,可以使用node做服務端,但是由於歷史原因,我們這個項目遷移起來也比較費時間,所以最後決定使用openresty來做。
openResty的教程網上很多了,我也不多說,除了官方git,推薦開濤博客學習。

不論我們是在客戶端取介面數據,還是服務端,活動頁的數據一般來說都有一定持續時長,也就是說,我們也可以在我們的nginx伺服器上做一個緩存,假設有一個用戶訪問了一個活動,那麼在接下來的五分鐘之內,其他任何用戶(相同許可權下)訪問到的就是第一個用戶訪問時緩存好的頁面。

local args = ngx.req.get_uri_args()
local acId = args['acId']
local key = 'ac' .. acId
local expire = 5 * 60 --緩存時間5分鐘

local value = dict.getData(key, expire)

if not value then
    local res = ngx.location.capture('/a.json')

    if res.status == 200 then
        -- dict是一個用來處理存儲和讀取邏輯的腳本
        dict.setData(key, res.body)
    else
        ngx.say(dict.getData(key))
    end
else
    --  ngx.log(4, type(value))
    ngx.say(value)
end

在dict中,我們可以把介面數據轉換成通過模板轉換為html片段存儲起來。這樣,用戶在第一次進入我們的頁面以後,也不會感覺慢了。

what's more

openResty能做的事不僅僅是這些,一些網關上許可權的控制等等都可以用它來實現。
上面兩個方案還有一些不足之處,比如首屏可能內容比較多,一次都載入過來,不一定會快。直觀的首屏的由服務端渲染,對於不重要的內容,可以通過AJAX來非同步載入。
如何計算活動頁這樣的頁面中,首屏有多大,如何組織代碼,哪些部分採用服務端渲染,哪些採用AJAX。這些問題在實際開發中也需要考慮。
限於時間,此次沒有能比較完善的解決這個問題,在日後開發中,還會繼續完善活動頁優化方案。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 開題:之前就有所耳聞,最近兩天第一次運用到圖標字體。剛開始嘛,一臉懵逼的狀態。成功運用之後就來記錄一下使用過程咯! 1. 打開線上生成工具:https://icomoon.io/app/#/select 2. 導入本地文件或者選擇圖標庫 (1) 如果你本地沒有.svg圖標,你可以選擇線上免費的圖標。 ...
  • Nodejs設計的核心理念:1.事件迴圈;2.模式;3.差錯處理;4.運用多處理器 ...
  • 時間關係長話短說,今天把文章編輯和刪除功能實現了。 本來是要單獨做兩個按鈕來選擇列表中的所有朋友圈文章,但是老師想偷懶……所以我也就跟著偷懶了。 編輯文章部分,可以獲取每條朋友圈的標題和內容。 第一步編輯的時候要打開編輯框,改動之後再保存至資料庫。 測試刪除這條數據。 有一個刪除的提示,也是用的el ...
  • 1)基礎 學習目的: 1. 客戶端表單驗證 2. 頁面動態效果 3. jQuery的基礎 什麼是JavaScript? 一種描述性語言,也是一種基於對象和事件驅動的,並具有安全性能的腳本語言 javaScript是一種基於對象和事件驅動的,並具有安全性能的腳本語言 解釋執行javaScript特點向 ...
  • 定義: 保證一個對象(類)僅有一個實例,並提供一個訪問它的全局訪問點; 實現原理: 利用閉包來保持對一個局部變數的引用,這個變數保存著首次創建的唯一的實例; 主要用於: 全局緩存、登錄浮窗等只需要唯一一個實例的時候; Part1、命名空間的管理員開發中經常會遇到不同的人定義的變數使用的單詞可能會重覆 ...
  • display的所有屬性 下麵就display的重要屬性進行講解,並配合一些相關的例子 基本屬性 display: none none 是 CSS 1 就提出來的屬性,將元素設置為none的時候既不會占據空間,也無法顯示,相當於該元素不存在。 該屬性可以用來改善重排與重繪,同時我也經常用它來做模態窗 ...
  • 使用遞歸函數必須要符合兩個條件: 1、 在每一次調用自己時,必須是(在某種意義上)更接近於解; 這句話怎麼理解? 大家家裡都有樓梯吧?比如從一樓走到二樓,那麼我們的起點是一樓,目的地是二樓,當你往上每走一個臺階是不是越接近二樓,也就是越接近目的地。 因此這句話可以這樣理解:函數每一次調用自己時... ...
  • 在 JavaScript 中 this 常常指向方法調用的對象,但有些時候並不是這樣的,本文將詳細解讀在不同的情況下 this 的指向。 一、指向 window: 在全局中使用 this,它將會指向全局對象,因為瀏覽器中運行的 JavaScript 的全局對象預設為 window, 所以,此時 ... ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...