zoukankan      html  css  js  c++  java
  • 离线包方案参考思考过程-总结了几篇文章

    总结了下几篇文章

    网易(资源离线/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加载逻辑都比较复杂, 重试页无法成功时, 会降级加载线上的详情页, 优先保证用户的体验.

    七.总结

    • 数据很重要: 优化加载之前第一件事, 就是建立一个详情页的数据看板, 只有通过数据, 我们才能了解目前线上用户的现状, 从真实用户的体验找到瓶颈和优化点.
    • 用户体验优先: 优化方案很多, 除了加载速度之外, 需要从整体应用体验出发, 选择对用户最佳的方案
    • 追求极致: 扣细节, 到极致.
  • 相关阅读:
    【递推】【HDU 2073】无限的路 (找规律)
    【省赛】山东省第七届ACM省赛(部分水题)
    【思维】牛客练习赛16 B-漂亮的树
    【搜索】牛客练习赛16 C-任意点 (类似求联通块)
    输入输出
    Python入门——运行python的两种方式&变量&常量
    编程语言分类
    What is an Operating System?
    计算机硬件&编程基础
    《SPA设计与架构》之认识SPA
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/14582448.html
Copyright © 2011-2022 走看看