# 離線包方案參考思考過程-總結了幾篇文章

總結了下幾篇文章

網易(資源離線/JsBridge通信/接口預請求)

一. 資源離線

  • 靜態資源加載耗時, 資源離線到本地, 能很好解決.

  • web頁面把靜態資源生成zip包, 客戶端在合適的時機拉去zip包並解壓到本地, 持久化存儲.

  • 用戶訪問的時候攔截WebView發出去的頁面請求, 直接返回對應的本地文件.

  • 前端:

    • 生成zip包 -> 更新離線數據
  • APP:

    • 下載zip包 -> 攔截頁面請求 -> 返回本地資源
  • 三個關鍵部分:

    • Web頁面Zip包生成工具
    • 離線管理系統
    • 客戶端離線實現
  • web打包工具

{
  [   
    "name": "index", // 頁面名稱
    "url": ["https://example.com/index"] //頁面線上地址 
    "zipUrl": "https://assets.example.com/static/example.20190525_1020.zip", // zip地址
    "md5": "md5md5md5md5md5md5md5md5md5md5md5md5" // md5
  ]
}
  • 工具自動化分成通過中間件實現

  • 通用部分:

    • 拷貝頁面依賴, 生成zip包
    • 判斷包的完整性
    • 獲取zip包的md5值
    • 生成zip包版本號
  • 定製部分:

    • 確定待更新zip包
    • 上傳zip包到cdn
    • 更新離線數據, zip包版本數據
  • 通用部分:

    • 獲取打包配置 -> 拷貝/打包 -> 檢測包完整性 -> 獲取MD5
  • 定製部分: (可以在打包工具做, 也可以手動上傳)

    • 確定待更新包 -> 上傳zip到cdn -> 更新數據
  • 離線管理系統:

    • 爲離線工具提供打包信息及離線包信息存儲
    • 爲App提供離線數據
    • 頁面離線數據在線管理
  • 應該完成多產品, 多用戶設計.

  • 工具自動更新數據, 還可以在系統裏添加數據, 對數據進行增刪改查.

  • 離線數據保留最近5個版本, 發現線上zip包有問題, 可以迅速回歸.

  • 核心功能:

    • 多產品
    • 多用戶
    • 在線操作
    • 提供接口
  • 客戶端實現 (最重要)

    • 離線資源更新
    • 攔截資源返回
  • 離線資源管理器總調度處理資源更新和攔截返回.

  • 根據配置離線配置細膩創建動態管理器, 部署每個url對應的頁面入口文件, 靜態資源目錄等.

  • 更新app配置氛圍主動和被動.

    • 主動通過app啓動後通過接口獲取離線配置信息.
    • 被動通過push更新.
  • 獲取離線配置後, 讀取本地配置緩存進行對比.

  • 根據頁面名稱確定離線文件的更新策略是什麼.

    • 遠端配置無, 本地配置有, 認爲當前頁面離線包被刪除. 直接刪除本地對應的離線頁面入口文件.
    • 發現兩個配置中同名頁面zip包的md5不一致, 認爲應該更新了.
    • 如果發現遠端有, 本地無, 則是新增, 然後交給下載管理器下載. 下載解壓完成後, 通知管理器更新本地配置.
  • 流程:

    • 獲取離線配置 -> 匹配資源 -> 確定更新策略 -> 更新資源和本地配置.
  • 攔截返回細節.

    • 統一攔截所有網絡請求, 通過管理器處理訪問邏輯.
  • 需要攔截返回:

    • html
    • js, css, img
  • app在WebView發起請求時, 會攔截當前頁面請求, 獲取頁面的URL地址, 根據管理器中的配置, 進行查找.

    • 找到直接返回入口文件
    • 未找到請求線上地址
  • 頁面的加載會伴隨着依賴資源的加載, 獲取請求url, 如果在攔截域名內, 則替換域名爲本地的靜態資源目錄進行查找.

  • 找到後, 獲取文件擴展名, 設置返回的文件類型直接返回.

  • 攔截並返回本地資源

    • & 返回本地資源
    • & 獲取離線配置 -> 匹配請求地址 -> -> 渲染Web頁面
    • & 請求線上資源
  • app針對每個環節出現的錯誤進行上報:

  • 離線相關的錯誤類型有:

    • 獲取離線配置接口網絡錯誤
    • 獲取離線配置接口數據解析失敗
    • zip包請求網絡
    • zip包解壓錯誤
    • zip包md5值app端與前端不一致
    • zip包解壓手機空間不足
  • 任何一種錯誤都不會更新本地離線資源和離線配置.

一. JSBridge

  • 大部分業務需要的native功能

    • 視圖層面: 註冊, 登錄, 認證, 註銷組件, 視圖路由
    • 存儲層面: 用戶信息, 設備信息, 業務狀態, 緩存
    • 網絡層面: 請求header, 代理轉發, 預請求
    • app層面: 喚起, 設置, push, 跨app操作
    • 系統層面: 底層api的調用
  • 其他輔助功能.

  • ...其他的不屬於離線包, 暫不處理...

一. 實際應用

  • 請求代理
    • 預請求
    • 統一業務header
    • 統一日誌管理
    • 跨域
  • WebView預創建

二.移動端本地 H5 秒開方案探索與實現》

二.爲什麼體驗糟糕

  • 過程:

  • 初始化webview -> 請求頁面 -> 下載數據 -> 解析HTML -> 請求js/css資源 -> dom渲染 -> 解析js執行 -> js請求數據 -> 解析渲染 -> 下載渲染圖片

  • 過程圖片

  • 一般頁面在dom渲染後才能展示, 可以發現, H5首屏渲染白屏問題關鍵: 如果減少從請求下載頁面到渲染之間這段時間的耗時.

二.如何優化

  • 優化常用方式:

    • 降低請求量: 合併資源, 減少HTTP請求數, minify/gzip壓縮, webP, lazyLoad
    • 加快請求速度: 預解析DNS, 減少域名數, 並行加載, CDN分發
    • 緩存: HTTP協議緩存請求, 離線緩存mainifest, 離線數據緩存localStorage.
    • 渲染: js/css優化, 加載順序, 服務端渲染模板直出.
  • 直接打包H5相關頁面到客戶端中, 然後客戶端將數據傳遞給頁面, 通過webView加載展示. 不需要網絡請求, webView只要渲染頁面, 執行js即可.

二.實現

  • H5和native通信

    • jsapi: 客戶端提供接口, 注入api讓js調用, 直接執行相應的native代碼, 適用於需要通過交互, 進行數據請求的場景
    • url scheme: web端發送url scheme請求, 之後native攔截請求並根據url scheme以及所帶參數進行相關操作. 使用頁面跳轉.
    • 字符串替換: 客戶端讀取本地H5後, 通過對h5中約定的標記位進行字符串替換. 然後加載展示頁面. 適用於沒有複雜交互, 只通過頁面渲染數據的場景.
  • 開發本地H5模塊, 本地模擬數據開發, 然後H5給各客戶端打包後聯調. 繁瑣, 因爲給客戶端打包時比較分散, 不統一, 管理困難.

  • 本地h5實現模塊的頁面建議一個統一git倉庫, IOS和android客戶端通過git submoduleGit Submodule使用完整教程將本地h5的git外鏈到項目中, 客戶端中的資源就可以統一管理了, 解放了每次都手動繁瑣替換打包工作.

  • H5資源給到後臺, 客戶端按照業務模塊預下載整個離線包, 離線包根據版本做增量更新.

  • 離線更新事宜圖

二.細節

  • 預加載webView, 預拉取數據
  • 屏蔽webView HTML內容自動識別
  • 點擊延遲
  • 國際化
  • WKWebView兼容
    • WKWebView性能相對UIWebView較好. 推薦使用
    • WKWebView加載本地的HTML時, 會有兼容問題, 在IOS8不能在HTML文件中引用本地的css或者js或者圖片文件.
    • ios8以上是正常的, 可以引用遠程資源.
    • ios8使用網絡資源, ios8以上使用本地資源.
    • ios8中, 使用遠程cdn的css或者js文件, 引用標籤必須添加charset屬性. 不然css和js亂碼.

三.螞蟻離線包簡介

  • 離線包簡介

  • 傳統的H5技術容易受到網絡環境影響, 因而降低H5頁面性能. 離線包可以解決該問題, 同時保留H5的優點.

  • 離線包是將包括HTML, JavaScript, css等頁面的內置靜態資源打包到一個壓縮包內. 預先下載該離線包到本地, 然後通過客戶端打開, 直接從本地加載離線包.

  • 優勢:

    • 提升用戶體驗: 通過離線包的方式把頁面內置靜態資源嵌入到應用中併發布, 當用戶第一次開啓應用的時候, 就無需以來網絡環境下載該資源. 而是馬上開始使用.
    • 實現動態更新: 在推出新版本或是緊急發佈的時候, 可以把修改的資源放入離線包, 通過更新配置讓應用自動下載更新. 無需通過應用商店審覈, 就讓用戶及時接收更新.

三.離線包結構

  • 離線包是一個.amr格式的壓縮文件, 後綴.amr改成.zip解壓後, 可以看到其中包含了HTML資源和JavaScript代碼等. 待H5容器加載完成後, 這些資源和代碼能在WebView內渲染了.
    • 一級目錄: 一般資源包的ID, 例如20150901
    • 二級目錄及子目錄: 業務自定義的資源文件. 建議所有前端文件保存在一個統一目錄下.
* 20150901
  * hmpfile.json
  * sdk
  * www
    * index.html
    * js
    * test.html
* 20150901.tar
* CERT.json
* Manifest.xml

三.離線包類型

  • 基礎通用庫使用全局離線包.
  • 類型:
    • 全局離線包: 包含公共資源, 可供多個應用共同使用
    • 私有離線包: 只可以被某個應用單獨使用.
  • 使用全局離線包後, 在訪問H5的時候, 都會嘗試在這個包讀取. 如果該離線包裏有對應資源的時候, 直接從該離線包裏讀取, 而不通過網絡. 因此全局離線包的機制主要是爲了解決對於通用庫的使用.
  • 由於要保證離線包的客戶端覆蓋率以及足夠的通用性. 此包一般更新週期至少爲一個月, 並嚴格控制離線包大小.

三.渲染過程

  • H5容器發出資源請求時, H5容器或截獲該請求:

    • 如果本地有資源可以滿足該請求, H5容器會使用本地資源.
    • 如果沒有可以滿足請求的本地資源, H5容器會使用線上資源.
  • 無論資源使用線上或者是本地的, WebView都是無感知的.

  • 離線包的下載取決於創建離線包時的配置

    • 如果"下載時機"配置爲僅WiFi, 只有wifi網絡時纔會下載.
    • 如果配置爲"所有網絡都下載", 會消耗用戶流量自動下載, 慎用.
  • 如果當前用戶點擊app時, 離線包尚未下載完成, 則會跳轉到fallback地址, 顯示在線頁面.

  • fallback技術用於應對離線包未下載完畢的長江. 每個離線包發佈時, 都會在CDN發佈一個對應的線上版本. 目錄結構和離線包結構一致.

  • fallback地址會隨離線包信息下發到本地. 在離線包未下載完畢的場景下, 客戶端會攔截頁面請求, 轉向CDN地址. 實現在線頁面和離線頁面隨時切換.

  • 渲染過程

三.離線包運行模式

  1. 請求包信息: 從服務端請求離線包信息, 存儲到本地數據庫過程. 離線包信息包括離線包的下載地址, 離線包版本號等.
  2. 下載離線包: 把離線包從服務端下載到手機.
  3. 安裝離線包: 下載目錄, 拷貝到手機安裝目錄.

三.虛擬域名

  • 虛擬域名僅對離線應用有效, 當頁面保存在客戶端之後, WebView通過file schema從本地加載訪問的. 然而用戶就能在地址欄裏直接看到file的路徑:
    • 用戶體驗問題: 當用戶看到file地址, 會對暴露的地址產生不安全感和不自在.
    • 安全性問題: 由於file協議直接帶上本地路徑, 任何用戶都可以看到這個文件所有的路徑, 會存在一定的安全隱患.
  • 所以採用虛擬域名的機制. 而不直接使用file路徑訪問. 虛擬路徑是一個符合URL Scheme規範的HTTPS域名地址, 例如: https://xxxxxx.h5app.example.com, 虛擬域名的父域名example.com一定得使用自己註冊的域名
  • 這個域名可以是網上註冊的, 但是一般情況下, 不建議將虛擬域名配置成互聯網一致的域名, 這個在判斷問題的時候, 容易增加判斷難度, 容易出錯不便於日常管理. 只要保證其父域名example.com域名是自己註冊的域名即可.
  • 標準的虛擬域名如下: https://{appid}.h5app.example.com

四.螞蟻生成離線包

  • 參考: 生成離線包
  • 根據不同需求, 將不同業務封裝成爲一個離線包, 通過發佈平臺發佈對客戶端資源進行更新.
  • 生成步驟:
    • 構建前端.zip包
    • 在線成才.amr包

四.構建前端.zip包

  • 根據場景不同: 配置路徑分爲:
    • 全局資源包
    • 普通資源包
  • 同一個H5離線包中, 全局資源包於普通資源包不可共存.
  • 離線包ID(下文中的一級目錄), 必須爲8位數字.

四.全局資源包

  • 可以將被其他多個資源包引用的通用資源放置在全局資源包內, 並按照下列規則指定包內的資源路徑

    • 一級目錄: 全局資源包的ID: 如77777777
    • 二級目錄: 指向資源可訪問的服務器域名地址.
      • 公有云: 固定爲mcube-prod.oss-cn-hangzhou.aliyuncs.com(離線包管理->新增離線包頁面的資源包類型中可看到相關提示信息)
      • 專有云: 請查詢專有云部署的mdsweb服務器域名地址
    • 三級目錄: appid_workspaceId, 例如: 53E5279071442_test
      • 三級目錄往後即爲業務自定義的公共資源文件. 在公共資源文件的文件夾名, 文件名以及文件中, 避免使用特殊字符. 特殊字符會被urlencode函數轉換字符
  • 以上規則組織資源文件後, 即可按照如下格式快速獲取資源文件的路徑:

    • 公有云: http://域名/appid_workspace/資源文件路徑
    • 專有云: http://域名/mcube/appId_workspace/資源文件路徑
  • 示例:

    • 在公有云中: 二級目錄固定爲: mcube-prod.oss-cn-hangzhou-aliyuncs.com, 所以下圖中資源文件common.js路徑爲: https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/53E279071442_test/common.js
    • 示例地址
    • 在專有云環境中, 二級目錄爲專有云部署的mdsweb服務器域名地址, 此外mdsweb-outer.alipay.net爲例. 下圖資源文件common.js的路徑爲https://mdsweb-outer.alipay.net/mcube/53E5279071442_test/common.js
    • 示例地址
  • 注意實現:

    • 公共資源的絕對路徑長度不超過100字符, 否則會導致客戶端加載資源失敗以及頁面白屏
    • 服務端未控制全局資源包版本, 用戶可根據實際需求, 通過在三級目錄以後添加文件目錄結構的方式, 來自定義控制文件的高低版本.
    • 在專有云環境中, 如果服務端採用的文件存儲格式爲HDFS或AFS, 則需要在上述第三級目錄前增加一個目錄, 該目錄名稱爲mdsweb目錄, 該目錄名稱爲mdsweb服務器中的存儲空間(bucket)的名稱.
    • 引用公關資源: 在普通離線包內訪問全局資源包中的內容, 必須通過絕對路徑訪問, 如: https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/53E5279071442_test/common.js
    • 示例

四.普通資源包

  • 按照業務將相關的HTML, CSS, JavaScript, 圖片等前端資源放置在同一個離線包內, 目錄結構如下:

  • 一級目錄 普通資源包的ID, 如20171228.

  • 二級目錄及往後即爲業務自定義的資源文件. 建議在所有的前端文件最好保存一個統一的目錄下, 如/www, 並設定當前離線包默認打開的主入口文件, 如: /www/index.html

  • 示例.

  • 配置萬資源包的路徑後, 即可直接將appid所在的目錄整體壓縮爲一個.zip包.

四.在線生成.amr包

  • 進入控制檯的實時發佈->離線包管理頁面, .zip包上傳到MDS發佈平臺, 生成.amr包.
  • 示例

五.H5秒開方案大全

五.常用的加速方法

  • 資源加載:

    • 針對首屏
    • 更小的資源包
    • 壓縮, 減包, 拆包, 動態加載包, 圖片優化
  • html渲染:

    • 針對可優化
    • 更快的展示內容
    • cdn分發, dns解析, http緩存, 數據預請求, 直出
  • rn, weex, flutter衝擊傳統hybrid, hybrid加速發展.

五.直出+離線包緩存

  • 直出: 後端渲染, 省去了ajax請求時間, 能夠通過各種緩存策略優化很好, 加載html扔需要時間.
  • 離線包技術: 解決html本身加載需要的時間問題.
  • 離線包基本思路通過通過webview統一攔截url, 將資源映射到本地離線包, 更新的時候對版本資源檢測, 下載和維護本地緩存目錄中的資源.
  • 渲染loading過程
  • 對於web端而言: 相對透明, 侵入性比較小.

五.客戶端代理的VasSonic 騰訊手 Q VasSonic 秒開

  • 用戶點擊到看到頁面之間, 存在webview初始化, 請求資源的時間, 這裏的過程是串行的, 所有存在優化空間.

  • 支持離線包策略, 並更進一步

    • webview初始化和通過客戶端代理資源請求並行
    • 流式攔截請求, 邊加載邊渲染
    • 實現了動態緩存和增量更新
  • 客戶端代理請求並行:

    • 創建webview之前, 通過客戶端代理建立網絡鏈接, 請求html, 然後緩存起來.
    • 等待webview線程發起請求html資源的時候,客戶端攔截, 將緩存的html返回給webview
  • 動態緩存和增量更新:

    • 自定義了一套標籤. 將html區分爲模板和動態數據兩部分.
    • 拓展了http頭部, 定製了一套請求後臺的約定
    • webview發情請求的時, 會將頁面內容的id攜帶過去, 後臺判斷後, 再告訴客戶端是否更新局部數據
    • 如果是緩存額html模板和新數據拼接成新的html, 最後計算差異部分, 通過js回調給頁面, 進行局部刷新
  • 示例

  • 通過模板可以達到局部變化, h5秒開結果.

  • 但定義了一套特殊的註釋標記及拓展了頭部, 需要包括後臺在內的前後端進行改造. 對web入侵性非常強. 維護成本高.

五.PWA+直出+預加載

  • 不管是離線包技術, 還是webview代理請求, 都對前端入侵比較大.
  • pwa能夠通過純web的方案優化加載性能.
  • 對於直出html, 配合pwa, 直出文件, 緩存到cacheStorage, 在下一次請求時, 優先從本地緩存中讀取, 同時發起網絡請求更新本地html文件.
  • 第一次加載通過app預加載一個js腳本, 拉去需要pwa緩存的頁面, 可以提前完成緩存.
  • 非直出頁面.
    • 第一次只能提前加載. 預加載腳本.
    • 第二次非直出頁面, 每個頁面需要有獨一無二的標記, 比如hash. 瀏覽器獲取到數據, 渲染好的html, 通過outerHtml方法, 將html緩存到cacheStorage中.
    • 第二次優先從本地獲取, 同時發起html請求, 通過對比其中唯一標識的差距, 決定是否需要更新.
  • 示例
  • pwa一系列方案替代離線包, 屬於web標準, 適用於普通能夠支持service-worker的H5頁面.
  • 在兼容問題允許的情況下, 建議主加.

五.NSR渲染

  • 前端SSR
  • 借住瀏覽器啓用一個JS-Runtime, 提前將下載好的html模板及預取的feed流數據進行渲染. 然後將html設置到內存級別的MemoryCache中, 從而點開即看.
  • NSR將SSR渲染過程分發到各個用戶的端. 減少後臺請求壓力, 進一步提高頁面打開速度.
  • 數據預取和預渲染帶來的額外流量和性能開銷, 特別是流量. 如何更準確的預測用戶行爲, 提高命中率非常重要.

五.客戶端PWA

  • service-worker在webview實現性能並沒有想象中好.

五.小程序化

  • 小程序內部將webview渲染和js執行分離開, 然後通過離線包, 頁面拆分, 預加載頁面等手段
  • 犧牲了web的靈活性.
  • 對於hybrid開發, 通過原生客戶端底層支持小程序環境, 大量業務邏輯採用小程序方案開發
  • 迭代速度和性能兼容, 是一個不錯方向

五.總結

  • 在整個鏈路中減少中間環境, 例如串行改並行, 包括小程序內部執行機制.
  • 儘可能預加載, 預執行. 比如從數據拉去, 到頁面渲染.
  • 任何轉換都有代價, 加速本質上就是在用更多的網絡, 內存和CPU換取速度. 以時間換空間

六.轉轉hybrid app web靜態資源離線系統

六.前言

  • 優點:

    • web頁面上線滿足快速迭代的業務需求, 不收客戶端審覈和發版的時間限制.
    • 也可以將各個業務線的開發工作分攤到各個業務的fe團隊上, 是的業務線並行開發.
  • 缺點:

    • web應用的性能和體驗, 不及客戶端.
  • 痛點1:

  • 打包後靜態資源過大, 首次打開/線上H5資源更新/網絡條件差/本地頁面緩存失效.

  • 白屏.

  • 痛點2:

  • app使用系統原生的web view, 不兼容pwa.

  • 各個業務團隊使用的技術棧比較廣. 各個業務線快節奏開發, 需要低成本接入. 對業務代碼不會產生入侵.

六.方案

  • 流程圖

  • 前端構建發佈:

    • ak-webpack-plugin: 根據配置, 將webpack的構建出的靜態資源, 壓縮成了靜態資源在cdn路徑url的zip包, 同時在配置的過程中, 可以選擇排除掉部分文件.(例如, 部分圖片)
    • 不需要關注資源之間的依賴關係, 更不需要關注具體的業務邏輯.
    • 只需要關注webpack構建後生成的資源文件夾的結構.
    • 使用jenkins.持續集成和發佈.
    • 流程圖
  • app:

    • 預置一份最新的各個業務線的離線包與版本號的配置表.
    • app啓動時, 會將壓縮包解壓到手機rom中, 各業務線配置中包含app訪問線上的靜態資源時需攔截的url規則map:
    • 當app訪問到與規則map相匹配的地址時, 就轉爲本地資源, 達到離線訪問目的.
[{
  "bizid": 13,
  "date": "1513681326579",
  "ver": "20171219185710",
  "offlinePath": [
    "c.58cdn.com.cn/youpin/activities"
  ]
}]
  • 流程圖

  • 離線資源如何更新

    • 客戶端啓動後, 向離線系統查詢最新的各個業務的離線包版本號, 依次跟本地配置中的對應業務線比較.
    • 如果需要更新, 則再次向離線系統查詢此業務線的離線包信息. 離線系統會提供此業務線的離線包的信息.

流程圖

  • 判斷是否需要更新:
    • 線上的各業務線的離線包版本號與本地配置中 同一業務線的配置不同(不論最新的離線包版本比本地更高或者更低)
    • 線上的各業務線的配置中包含本地配置沒有配置的業務線.

六.離線包加載優化

  • 增量的資源更新 (bsdiff/bspatch)
    • 影響bsdiff生成的差分包的體積因素主要有:
      • zip包的壓縮等級
      • zip包中文件內容的修改. 比如js進行了uglify壓縮, 變量名的變化可能引起大幅的變更.
    • 可以減少客戶端升級離線資源需要下載的流量
  • 單獨控制各個業務線web應用是否使用離線機制
    • 每個業務都加入使用離線資源的開關和灰度放量的控制.
  • 數據一致性校驗 與 數據安全性校驗.
    • 防止下載離線資源在傳輸中被篡改, 使用md5驗證.
    • 同時保證傳輸過程中, 資源文件不被篡改, 將md5值通過rsa加密算法進行加密, 在服務端和客戶端分別使用一對非對稱的密鑰進行加解密.
  • 批量下載:
    • 啓動app後, app會集中批量下載各個業務線的離線包資源, 在cdn中使用http2協議, 一次鏈接, 下載所有資源. 離線包個數較多的情況下, 可以比傳統http1有更快的傳輸速度, 同時, 客戶端只需要運行一次下載器. 減少多次運行下載對手機cpu損耗.

六.回退機制 fallback

  • 可能出現的問題:
    • 本地內置的base包(zip文件)解壓失敗
    • 離線系統接口超時
    • 下載離線資源失敗
    • 增量的資源合併失敗等情況

六.離線資源管理平臺

  • 管理系統演示

  • 管理系統演示

  • 功能:

    • 查看和管理各個業務線信息及其離線功能的灰度放量比例.
    • 通過業務線, 版本號, 發佈時間等條件. 查詢各個版本的離線包資源列表及其詳細信息
    • 針對全局離線功能, 提供了離線功能開關.
    • 允許將某個版本的離線包下線, 以實現離線資源版本的回滾功能.

六. 技術選型

  • 離線系統的服務端使用nodejs實現.
  • 使用輕量的koa框架.
  • 使用log4j進行node日誌的採集和記錄.
  • 使用輕量的nosql數據庫mongdb記錄離線包的數據信息. 使用對象模型工具mongoose進行nosql操作
  • md5的加密, 使用node-rsa庫進行非對稱密鑰的生產, 操作和加密解密處理.
  • 前端離線系統的後臺頁面, 使用vue與組件庫iview.
  • 壓力測試: 使用壓力測試工具siege.
  • 建立性能監控系統, 運行穩定性/承載的壓力/佔用服務器硬件資源情況.

六.運行情況

  • cpu使用率
  • 應用內存使用量
  • 頁面靜態資源加載(js, css)耗時
  • 頁面可操作時間耗時

六.展望

  • 下載引擎優化
    • 斷點續傳/分塊下載
  • 離線資源的統計
    • node api層/cdn的nginx層實施
  • pwa技術
    • 通過接入其他更強大的瀏覽器內核實施

七.今日頭條品質優化:圖文詳情頁秒開實踐

七.數據建立

  • 頁面加載時間 = 頁面加載完成時間 - 頁面開始加載時間

  • 頁面開始加載時間: 點擊了Feed上的卡片.

  • loadFinish回調不能反映用戶的真是體驗.

  • WebView渲染步驟:

    1. 解析HTML文件
    2. 記載JS和css文件
    3. 解析並執行js
    4. 構建DOM結構
    5. 加載圖片等資源
    6. 頁面加載完成
  • 用戶真實角度: dom結構構建完成(domReady)的時間點作爲頁面加載完成

七.白屏

  • 對webview進行截圖, 遍歷截圖的像素點的顏色值.
  • ios提供了webview快照的接口獲取當前webview渲染的內容, 底層異步回調, api耗時10ms.
  • android中提供獲取視圖內容結構爲getDrawingCache, api耗時40ms
  • webview截圖的圖片進行縮小到原圖1/6, 遍歷檢測圖片的像素點, 非白色像素點大於5%的時候, 認爲非白屏情況, 可以相對高效檢測準確得出詳情頁是否發生了白屏

七.指標建立

  • 明確問題: 什麼指標可以反映用戶刷頭條時的真實體驗

  • 最開始: 頁面平均加載時長: 頁面加載時長總和 / 頁面pv

  • 平均加載時長雖然可以反映詳情頁加載速度, 但因爲詳情頁的pv比較高. 如果使用平均加載數度, 很多用戶體驗問題, 都被忽略掉了. 並不能反映用戶的真實情況.

  • 調整口徑: 所有用戶進入詳情頁的80分位值

  • 80分爲值是1s, 說明80%用戶進入詳情頁都能在1s內加載完成.

  • 優化目標爲 80%用戶, 0.3s加載完成.

  • 數據示例

  • 後來提高到95分位

七.模板優化

  • 模塊拆分:

  • 點擊後的過程: 點擊過程

  • 線上頁面加載用戶每次進入詳情頁都要多次網絡加載, 極容易受到網絡波動影響, 無法保證頁面加載的時長和成功率, 極大的影響用戶體驗.

  • 於是, 在頭條中, 我們將新聞中標題和正文內容分開, 把頭條詳情頁的公共樣式css和邏輯js都抽離出來, 形成一個獨立而完備的詳情頁模板, 可以把模板內置到客戶端中(這不就是離線包嗎.)

  • 同時和前端約定好js腳本, 通過接口將正文內容數據注入頁面完成詳情頁的頁面展示, 通過這種方式可以將接口放到客戶端上請求.

  • 示例

  • 客戶端通過一定策略預加載新聞數據, 在理想狀態下用戶進入頁面看到頁面時,就可以直接使用緩存的數據. 用戶在看新聞的時候可以完全實現離線化, 避免受到網絡的影響.

  • 模板預熱:

  • 全流程離線化之後, 頁面加載的瓶頸變成本地模板加載, 優化本地模板加載.

    • 模板合併: 加載完html再去加載js和css, 需要多次io操作, 於是把js和css還有一些圖片都內聯到一個文件中, 加載模板就只需要一次io操作.
    • 模板簡化: 將非必須的腳本異步拉取, 精簡不必要的樣式和JS代碼, 將模板大小壓縮了20%以上.
  • 模板和數據分離後, 用戶點擊的時候加載的是同一個模板, 實際上, 我們並不需要在用戶進入頁面的時候去創建webview以及加載模板

  • 只需要在合適的時機在後臺創建webview, 並且提前預熱模板.

  • 用戶進入頁面的時候, 就能使用已經加載好模板的webview, 直接將詳情頁的內容數據通過js注入到頁面中. 前端收到數據, 渲染即可.

  • 模板預熱

  • 模板複用:

  • 95分位看實際數據優化並不明顯, 從數據上觀察, 用戶預熱模板的命中率只有53%, 可進一步提升.

  • 爲了儘可能提高頁面加載速度, 我們希望用戶每次進入詳情頁都能夠使用預熱好的webview, 模板預創建池手段優化用戶進入詳情頁時的預熱模板命中率.

  • webview的創建是一個性能開銷比較大的操作, 使用雨創建池的方案, 就會在後臺頻繁創建webview, 這樣對用戶在feed場景的瀏覽提體驗也會有一定影響.

  • 每次使用同一個模板, 用戶退出頁面後, 把正文數據清空, 進入下一個頁面的時候能夠繼續複用這個webview, 重新注入數據即可

  • 過程圖

  • 避免了後臺創建webview對用戶刷新feed體驗, 又提高了預熱模板命中率.

七.網絡優化

  • CDN加速
  • 容災

七.渲染優化

  • 服務器端預渲染:

    • 服務器端把所有的詳情頁正文的HTML數據組裝好, 通過將服務端直出內容注入到頁面中. 直接給webview渲染.
  • 客戶端渲染:

    • webview渲染非文字部分存在一下問題:
    • 渲染效率差
    • 大量圖片, webview的渲染內容佔用, 滑動體驗
    • 多次打開同一篇文章, 多次加載問題, 無法與客戶端進行緩存共享.
    • 圖片和視頻等非文字內容通過原生組件放在客戶端進行渲染, 提高渲染效率, 減少流量
    • 多圖文章, feed頁面, 只能加載詳情頁需要的圖片, 增加用戶的文章首屏體現.
  • 白屏優化

    • ios中, 我們使用系統自帶的WKWebView, 一個運行在獨立進程中的組件, 所以, 內存過大的時候, WKWebView所在的WebContentProcess會被系統kill掉, 反映出來就是白屏
    • 根據WKWebView提供的回調webViewWebContentProcessDidTermainate函數進行reload重新加載. 因爲是模板, 沒有辦法注入數據.
    • 嘗試重新注入時間很長. 在注入腳本中判斷是否存在數據注入接口, 如果不存在, 直接重試.
    • android, 使用自研內核webview
    • 多線程讀模板問題, webview在運行中會讀取的文件模板, 此時如果另一個線程同時更新模板文件, 就出現了模板加載問題, 需要保證模板加載的原子性.
    • Render卡死問題, 內部渲染可能會出現Render卡死問題. 從業務上做白屏監控進行重試.
  • 不管是IOS和Android, WebView加載邏輯都比較複雜, 重試頁無法成功時, 會降級加載線上的詳情頁, 優先保證用戶的體驗.

七.總結

  • 數據很重要: 優化加載之前第一件事, 就是建立一個詳情頁的數據看板, 只有通過數據, 我們才能瞭解目前線上用戶的現狀, 從真實用戶的體驗找到瓶頸和優化點.
  • 用戶體驗優先: 優化方案很多, 除了加載速度之外, 需要從整體應用體驗出發, 選擇對用戶最佳的方案
  • 追求極致: 扣細節, 到極致.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章