微信号:frontshow

介绍:InfoQ大前端技术社群:囊括前端、移动、Node全栈一线技术,紧跟业界发展步伐。

iOS新闻类App内容页技术探索

2018-06-07 17:40 朱德权
作者|朱德权
编辑|覃云

作者 GitHub :

https://github.com/dequan1331/HybridPageKit

据相关数据显示,截至 2017 年底,中国手机新闻客户端用户规模达到 6.36 亿人,移动 App 已经成为新闻和内容传播的最重要途径之一。而伴随着行业的竞争和发展,App 中的内容页在提升 App 品质、提升使用时长及提升用户黏性等方面,扮演着更为重要的角色,同时也面临着更大的挑战。
  1. 内容页在呈现上越来越丰富。新闻资讯作为内容页的主体,逐渐增加了更多的文字样式、内容形式、富媒体、以及广告、投票等更为丰富的元素。

  2. 内容页需要更多扩展区域来提高使用时长及用户黏性。在资讯主体之外,各个 App 逐渐打造了例如关注模块、推荐阅读模块、评论模块、运营模块等越来越多的扩展阅读区域。

  3. 短视频、直播的争夺越来越激烈。越来越多的新闻 App 都将视频作为独立的模块和独立的内容页进行展示。

  4. 同质化产品竞争激烈。要求更快的迭代速度、更优质的用户体验、更小的实现成本。

所以,新闻类 App 内容页架构的设计和技术的优化,也要配合产品形态的发展,在越来越复杂的需求挑战下,拥有快速响应的能力和稳定优质的体验。

本文结合分析目前主流(DAU)新闻类 App 如今日头条、腾讯新闻、天天快报、一点资讯等内容页技术方案的选择,一起探索新闻类 App 内容页的技术实现和优化。

概念定义

结合目前主流的内容页实现方式,我们把内容页分为上下两个部分,为了方便后续的阅读先简单定义下关键的名词。

  1. 上部分通常用 WebView 实现。常规包括标题 + 作者(关注)+ 资讯内容,我们称为 WebView 内容区。

  2. 下半部分主要是平行于 WebView 的各种扩展内容,常规包括点赞打赏、广告推广、相关推荐,热门评论等等,我们称为 Native 扩展区。

3.WebView 中每个复杂 UI 呈现、扩展区中每个独立模块,我们都称为一个模块或组件。

完整来看,整个内容页右侧(右滑)普遍为评论页。无论是之前流行的 ScrollView 右滑还是近期流行的 Push 新页面,这两种方式实现起来都比较简单且较为独立,故本文暂时忽略右侧(右滑)评论的部分。

目录

技术方案选择
WebView 类型选择

不同于微博,新闻类 App 的内容以段落性的文字为主,配合段落间的图片、富媒体等。同时为了满足跨平台的一致呈现、PC 网页的文章转载、不同平台文章的抓取,以及注重阅读而非交互等原因,使用 WebView 加载渲染本地的 HTML 字符串数据已经成为了新闻类 App 通用的方案。

  1. UIWebView VS WKWebView

稳定性

UIWebView 较多的 WebCore、JavaScriptCore Crash,以及系统性的内存泄露导致 OOM,对整个 App 的稳定性都是极大的隐患。反观 WKWebView,基于独立进程,不会占用 App 的内存计算,同时也不会导致主 App Crash。所以在系统级的稳定性上,WKWebView 有着极大的优势。

加载速度

WKWebView 通过 JIT 大幅优化了 JS 的执行速度,但是对于新闻类 App 内容页的使用场景来说,简单的进入、退出页面,且单纯的加载渲染 HTML 字符串,WKWebView 比 UIWebView 慢了很多(Benchmark)。

兼容性

NSURLProtocol 的无法使用、长按 MenuItems Bug(before iOS11)、iOS8 不能删除 Cache、设置 Cookies 及 UA、POST 参数、异步执行 JS…这一系列的问题,成为了稳定项目替换 WKWebView 最大的挑战。

扩展性

WKWebView 具有更加丰富的接口、更多 HTML 和 CSS 的支持、以及更加友好的 JS 交互。同时 Api 的持续更新和社区的活跃,从长远使用的角度看有着极大的优势。

  2. 修复、扩展 WKWebView

通过以上的分析,WkWebView 从系统级的稳定性、性能以及后续扩展性都有很大的优势。通过 WKWebViewExtension 扩展修复原生 WKWebView,结合 HybridPageKit 中 WKWebView 的回收复用逻辑,极大程度上解决了原生 WKWebView 的问题,起到了很好的效果。

修复扩展的问题

通过逐阶段分析耗时,在内容页的使用场景下,WKWebView 从 alloc 到准备开始渲染这段时间,有着极大的优化空间。在浏览内容页这种场景下,HybridPageKit 中通过 WKWebView 的复用回收以及资源缓存,极大降低了 WKWebView 加载渲染 HTML 的时间,使之低于原生 UIWebView。

通过私有方法的扩展和代码优化,在 WKWebViewExtension 中支持了 URLProtocol、修复了 MenuItems 的 bug、支持 iOS8 清理缓存、扩展安全的 JS 执行方法、以及扩展 NavigationDelegate 以兼容 JSBridge 逻辑等。

无需解决的问题

对于新闻类 App 内容页的使用场景,一些 WKWebView 的问题并没有必要形成通用的解决方案以兼容代码。比如 POST 请求不能带参数、Javascript 异步执行等问题,都可以通过代码的重构来进行解决。尤其不推荐卡主 Runloop 从而同步 JS 的方式。

遗留问题

目前,在使用 WKWebView 的过程中,唯一未解决的问题就是可靠、全面的白屏检测方案,从而支持 WKWebView 在任何情况下的 Crash 进行重载。诸如系统 Crash 回调、WebView Title 监听、ContentSize 监听、甚至屏幕随机取色值等方法都不能满足全部的白屏场景。

WebView 内容区与 Native 扩展区的衔接

对于目前的主流 App 来说,单纯的 WebView 已经无法满足复杂的呈现和逻辑。如何在页面中合理的处理 WebView 与扩展区中的多种 View 协同滚动,灵活扩展,并且支持下拉刷新、上拉加载等操作,不同的新闻类 App 也有不同的技术方案。

  1. 结合 TableView

实现原理:

由于扩展区中列表类型的模块较多(例如相关文章、评论等),最简单的实现即 Native 扩展区的模块拆分到 Cell 的粒度,整体使用 TableView 实现。对于扩展区和 WebView 的衔接,如上图一般有两种实现方案:TableView 根据 WebView 的 Inset(或 Div 占位)插入到 WebView 中 & WebView 作为 TableView 的 Header。

优点:

这种方法相对简单,容易实现内容页各个模块的布局,同时基于 TableView 的刷新逻辑,也能动态的处理各个模块的更新、插入删除,并且支持家在更多等。和 WebView 的结合滚动也较为流畅。

不足:

这种方式将 Native 扩展区的模块粒度都区分到 Cell 的层级,列表类型模块只能通过 Cell 或者以 Section 的模式进行管理,同时也无法跨页面的整体复用 UI 及业务逻辑。UI 的布局依赖 TableView 模式,灵活性较差。随着组件类型的增多,非同质性的 View 也没有充分利用 TableView 的复用。

同时无论使用哪种方式和 WebView 衔接,都影响了 WebView、TableView 的独立渲染展示,增加了维护的困难。并且 Header 与 Inset 对于头部区域的扩展,如下拉刷新等,实现都较为困难。

  2. ScrollView 嵌套

实现原理:

这种实现用一个 ScrollView 作为 Container,将 WebView 及扩展区的组件分别作为 SubView。全部 SubView 禁止滚动,内容页的全部滚动都发生在 Container 上。对于 SubView 中的滚动视图,如果 ContentSize 小于屏幕高度,则作为普通 View,否则设置为屏幕高度,通过 offset 和 Frame 的计算,动态的调整视图相对 Container 的 Frame 以及自身的 ContentOffset,实现滚动效果。

优点:

这种方式完全独立每个模块的实现,使 UI 和业务逻辑一一对应。对 WebView 的渲染没有干扰,模块的加载和布局灵活管理、复用,模块业务逻辑独立内聚。添加删除模块、实现上拉下拉等操作简单。极大的提高了灵活性和复用的可能。

不足:

由于这种方式需要对 SubView 中的滚动视图进行计算、模块动态更新时整体布局也需手动刷新等,极大的提高的实现的复杂度。

基于 ReusableNestingScrollview,在 HybridPageKit 中,封装了以上 ScrollView 嵌套逻辑。这样就隐藏了复杂的实现逻辑和边界条件,充分的保留了灵活性的特点。同时对于内容页的使用场景,精简了嵌套滚动的使用,扩展上拉加载更多及下拉刷新逻辑,使整个方案实现简单、灵活扩展。

WebView 内复杂 UI、复杂交互模块的展示

随着核心的 WebView 内容区逐渐支持复杂的呈现方式,单纯的 H5 基础渲染已经满足不了现有的需求,比如视频的交互、音乐的续播、以及各种地图、投票等组件。同时 Web 中复杂的 UI 和逻辑也极大降低了 WebView 的渲染速度,增加了开发和维护的成本。

  1. 复杂 UI 及逻辑实现困难

为了满足更好的交互体验,资讯内容中富媒体内容逐渐增多,如视频的续播、小窗播放、音乐悬浮播放、内容中插入地图、投票等。同时随着产品功能的迭代,例如图片类型的简单模块,也增加了点击全屏、长按保存、二维码识别、双击扩大等交互。这些复杂的 UI 和逻辑导致 CSS 和 JS 增多,Native 和 Web 的通信增加,以及大量运用 LocalStorage 等浏览器存储,增加了客户端开发和维护的成本。

  2. 简单图片的展示耗时

对于内容 WebView 中的图片,最简单的作法,就是后台直接下发 Img 标签,依靠 WebView 自身的下载与渲染。但是这种方式灵活度较低、客户端无法合理的控制下载时机、无法做自定义的缓存以及裁剪等。

对于简单 Img 标签的升级,即后台数据单独下发图片数据,客户端根据需求自定义选择下载时机及缓存策略。Html 模板中先用占位图占位,Native 下载成功后替换标签的 Src 进行展示。这种方式虽然解决了灵活性的问题,但是也带来了整个流程的复杂性,以及多次 IPC 间的通信延迟。

为了兼顾灵活性,以及缩短图片的 Loading 时间,我们在单独处理图片的同时,替换内容 WebView 中全部图片为 Native,减少不必要的流程及通信,极大提高了加载的速度。

  3. Native 化全部非文字类组件

为了减少实现复杂 UI、复杂交互模块的开发、维护成本、减少模块在 Web 和 Native 间的逻辑流程,提高 Web 中模块的加载展示速度,在 HybridPageKit 中将 Web 中全部非文字类模块全部 Native 化。

页面模板使用空 div 占位:

结合后台的模板与数据,全部模板中全部非文字类的组件,映射成统一 Class 的 Div,通多唯一的 id 与数据绑定。组件默认实现占位图逻辑,对于同步数据同时设置组件的 Size,异步数据则先设置为 0。替换后 WebView 对模板进行渲染。

渲染完成通过 JS 获取位置:

WebView 渲染成功回调,通过 JS 获取全部统一 class 对应 WebView 的 Frame,以及对应的唯一 Id。

在相应位置粘贴 NativeView:

在进行以上两个步骤的同时,进行下载图片数据、NativeView 创建、初始化、异步数据拉取等工作。在 JS 回调全部位置时,根据位置及 ID,粘贴 Native 组件。

调整字体大小,组件异步数据拉取:对于异步的变化,在复用逻辑之后,下文将结合一并说明。

内容页全部组件的滚动复用
  1. 主流滚动复用框架

继承特殊 ScrollView:

目前流行的框架如 alibab 的 LazyScrollView,对于实现复用回收机制,都需要继承相应的 ScrollView,这种方式对于 WKWebView 来说,是无法实现的。

继承特殊 Model:

由于滚动复用需要保存 View 对应的数据信息,大部分开源框架需要继承特殊数据 Model,生成对应必要的参数或方法,对于支持多种类型组件的通用框架来说,继承的实现方式不易于扩展和维护。

View 滚动状态简单:

滚动时位置的计算,最简单的方式就是根据屏幕的高度计算是否进入屏幕,对于预加载的需求,绝大部分开源框架也是只是在屏幕区域的上下增加了 Buffer,仍然不能区分具体的状态,如进入 buffer、进入屏幕等,无法满足复杂的业务逻辑。

  2. WebView 中组件的滚动复用

无需继承:

在 ReusableNestingScrollview 中,为了兼容 WebView、ScrollView 等一切滚动视图中子 View 的复用回收,我们通过 scrollView delegate 的扩展分发,扩展 handler 单独处理子 View 的复用回收,这样就在无需继承的前提下,支持所有滚动视图中子 View 的复用回收。

数据驱动:

由于 View 需要不断的复用回收,所以数据、状态、位置、对应的 View 类型都存储在对应的 Model 中,不但实现了数据驱动易于动态扩展,同时优化了复用的逻辑,也缓存住了 Frame 等关键信息优化了渲染布局逻辑。

面向协议:

由于滚动复用的模块对应的 View 及数据 Model 种类众多,在不动态扩展 NSObject、UIView 的情况下,无法做到通用的逻辑公用。所以为了更好的支持扩展、更灵活的实现方式,ReusableNestingScrollview 中面向通过扩展数据 Protocol,使得任何 Model 轻松实现复用回收对应逻辑。

更加丰富的状态:

在 ReusableNestingScrollview 中,为了满足更复杂的需求,如视频预加载及自动播放、Gif 预加载及自动播放等,我们扩展了组件在滚动过程中的状态,增加自定义 workRange,使组件在滚动过程中的状态变为 3 种,即 None、prepare 区域及 Visible 区域,更加全面准确的记录状态切换,更加灵活的支持业务场景。同时通过 3 种状态扩展为二级缓存,对 View 在不同级别的缓存设置不同的策略。

综上,通过 ReusableNestingScrollview 只需将模块对应 Model 扩展增加协议,滚动视图扩展 Delegate,就可实现任何滚动视图中子 View 的回收复用功能。

  3. 内容页中全部组件的滚动复用

在解决了内容 WebView 中非文字类组件的 Native 化、滚动复用之后,我们将实现思想运用到包含 Native 扩展区的,内容页整体架构中。如果从内容页的维度去看,内容 WebView 也可以算作一个组件,它和扩展区的各种组件一起作为 Container 的子 View,也可以运用上面提到的 ReusableNestingScrollview 进行实现和管理。

所以整个内容页就是从两个维度、运用 ReusableNestingScrollview 中的实现方法两次实现滚动复用回收、数据驱动、组件自管理以及组件状态切换逻辑。

组件异步拉取与动态调整

面对复杂的需求、以及按需加载、异步拉取等优化体验的策略,在 HybridPageKit 中也针对相应的场景做了高效的处理。

  1. WebView 字体大小调整

当 WebView 中字体大小调整时,需要同时调整全部 Native 组件的位置。我们监听 WebView 的 ContenSize 变化,当变化发生时,重新执行获取组件位置的 JS 语句获得全部组件的新位置。基于滚动复用的逻辑,只需要对在屏幕中的组件 View 的位置进行调整,其余只需要重新对组件对应 Model 的 Frame 进行赋值,极大提升了效率。在此基础上,要动态的检测 ContenSize 是否小于屏幕高度,高度小于一屏幕时,要同时调整 Native 扩展区组件的位置。

  2. WebView 中组件异步拉取数据渲染

对于异步拉取数据的组件,由于初始化时占位 Div 的高度为 0,当数据获取成功,并渲染好组件后,需要首先执行 JS 动态修改对应占位 Div 的大小,之后按照以上的逻辑,重新赋值 Native 组件位置。

  3. Native 扩展区组件异步拉取数据渲染

Native 扩展区中的组件不同于 WebView 中的组件,不依赖 WebView 自身渲染。所以当动态调整大小时,之需调整全部 Native 扩展区组件数据 Model 中保存的 Frame 信息,同时调整在屏幕中的组件位置即可。

内容页组件化架构

在实现了以上技术关键点的基础上,如何合理的设计内容页通用的架构,快速响应内容页的各种需求调整,使整体架构易扩展、易维护,同时有较高的性能及较小的内存占用,成为了整个内容页架构实现的重点。在 HybridPageKit 中,我们围绕灵活复用、高内聚低耦合、易于实现扩展三个重点的方向,设计实现了基于组件化的内容页整体架构。

组件化解耦及组件通信

为了满足内容页业务的相对独立,支持快速响应迭代及组件整体复用,内容页整体的结构应满足通用性、易于扩展、以及高内聚低耦合的特点。所以在 ReusableNestingScrollview 的支持下,采用组件化的方式实现全部内容页业务模块。

  1. 组件化解耦

为了达到组件的高内聚、与内容页的低耦合,在 HybridPageKit 中拆分业务逻辑为独立的组件化的处理单元,每个处理单元通过 MVC 模式实现。其中 Model 作为组件的数据,只需要在实现解析逻辑同时,实现对应 delegate 即可。Controller 只需要实现组件间通信的 delegate,选择性的实现例如 controller 生命周期、webview 关键回调、以及滚动复用相关的方法即可。通过组件的自管理及复用,组件可以集成统一的上报逻辑、业务逻辑到自己的 Controller 中,并且在不同类型的页面灵活复用。

  2. 组件通信

为了更好的实现组件化的结构,组件的 Controller 需要在内容页初始化时进行注册。内容页在每个关键的生命周期或业务节点,采用中心化通信,广播执行相应的方法,组件的 Controller 按需实现处理即可。对于新增、删除功能,只需扩展 delegate 中的方法,内容页中触发方法、组件中实现方法即可。

组件及 WebView 的复用管理
  1. WebView & 组件 View 全局复用

为了提高 WKWebView 渲染速度,通过建立全局 WKWebView 复用回收池来复用 WKWebView。除了基本的线程安全、复用状态管理等,在进入回收池前要 load 特殊 Url 以维护整个 backFowardList。组件的 View 也是通过全局的复用回收池进行管理,使得相同的组件 View 可以灵活的出现在内容页、列表页等 App 内各个页面,极大的减少了开发成本,提高运行效率。

  2. 自动回收 & 内存管理

WebView 及组件 View 实现自动回收逻辑,每次在申请新 View 时检测活动队列中 View 的 SuperView 是否为 nil,是则自动回收防止内存泄露,同时增加 View 最大数量阈值、内存告警自动释放逻辑等。

内容页整体架构
  1. 易于扩展业务节点 & 组件类型

对于增加关键的业务节点用于组件业务处理,我们只需扩展 delegate 中的方法,在相关组件中实现。内容页 Controller 中在相应位置,通过统一函数触发广播代理方法即可。对于增加组件来说,只需创建组件完全独立的 MVC 代码,实现数据解析 Model 并实现滚动复用 delegate,在组件 Controller 中实现 delegate 中需要的方法等待调用,以及初始化时在内容页注册即可。删除组件完全无需操作内容页,删除独立的 MVC 结构并停止注册即可。

  2. 易于扩展内容页类型

为了实现内容页扩展区的灵活复用,在 HybridPageKit 中也扩展了非 WebView 类型的内容页。就像文中之前提到的,如果将 WebView 看做一个整体作为一个组件,基于 ReusableNestingScrollview 的位置动态管理,完全可以替换成普通的 View(类似 Banner 视频内容页),或者可扩展收起的 View(问题回答页面)甚至 tableView 等。所以整个 App 内各种类型的内容页只需要简单的配置,便可进行实现和组件复用。

  3. 内容页架构

结合 ReusableNestingScrollview、WKWebViewExtension 以及组件化的设计思路,HybridPageKit 整体的架构如下:

通过继承特殊的内容页 Controller 并进行简单的配置,即可生成不同类型的内容页整体架构。框架内集成基本的 Mustache 解析和渲染。结合后台数据,只需实现对应页面中组件 MVC 逻辑即可。其中 Model 只需实现对应 Protocol,Controller 在内容页中注册,实现对应 Protocol 即可。

首屏加载速度优化

新闻类 App 内容页,在 Native 的页面框架下,基于 WebView 进行加载和渲染。所以,从优化的角度就延伸出两个维度,即从 Web 的维度优化,以及从 Native 的维度优化。

Web 维度的优化

WKWebView 的复用 :

通过 WKWebView 的复用,极大的缩短了 WebView 从创建到渲染结束的时间。

利用 HTTP 缓存 :

对于内容 WebView 中必要的 CSS 以及 JS,以及必要的基础 Icon,可以通过设置 HTTP 缓存,依靠浏览器自身缓存提高效率。同时通过资源 md5 校验以保证刷新资源。

减少资源请求并发 :

通过 Native 化全部非文字类的内容,Web 页面只加载最近本的 Html 内容,减少了业务逻辑的资源请求和并发。

减少 Dom & Javascript 复杂度 :

通过 Native 化全部非文字类的内容,极大的减少了 Dom 的复杂度、CSS 的复杂度以及过多的 JS 业务逻辑。

其它 Web 优化通用方法 :

精简 Javascript,使用 iconFont,CSS & Javascript 文件压缩等

Native 维度的优化

数据模板分离,资源并行加载 :

基于后台数据以及 Native 化组件,内容页 Html 中模板与数据分离,使得全部资源如图片视频等都可以通过 Native 在合适的时机异步并行加载。不依赖与 Web 的渲染。

预加载数据, 延迟加载组件:

对于内容页关键内容(Webview)的拉取,大部分 App 都放到了列表页中进行。进入内容页时直接从 Cache 中取出内容模板,直接交给 WebView 渲染。基于 ReusableNestingScrollview 扩展丰富的状态及二级缓存,在页面滚动的过程中各个组件也可以精确的实现按需加载、预加载等逻辑。

Native 化非文字 UI,及组件化实现负载均衡 :

WebView 中非文字类 UI Native 化,极大的缩短了展示所需的流程,减少了进程间通信,减少了 I/O 及图片编解码逻辑,提高了类似图片类的 UI 展示速度。

组件的解耦与自管理,以及广播 delegate 的实现,为组件的按需加载、按优先级加载提供了基础。对于内容页的各个组件来说,在内容页展示之前大部分是不需要初始化、数据拉取以及渲染的。组件化之后的组件可以根据业务优先级,在不同的关键生命周期回调中实现业务逻辑,以减轻内容页创建、模板拼接以及 WebView 渲染的压力。简单的举例,由于内容 WebView 几乎都大于一屏,扩展区中的全部组件都可以在 WebView 渲染结束后进行 View 创建、网络拉取和渲染等,这样即不影响用户的使用,同时极大的释放了渲染结束前的网络、IPC 及 CPU 压力,提高首屏展示速度。

组件的滚动复用 & 全局复用 & Model 缓存 Frame:

基于 ReusableNestingScrollview 扩展数据 Model,缓存对应 View 的 Frame 信息,结合 View 的滚动复用,极大的减少了 UI 布局的逻辑和计算。页面内组件的滚动复用及页面间的组件复用,也同时减少了组件 View 的初始化耗时。

其它通用方法:

基于 App 的技术实现和业务逻辑的优化,如异步执行业务逻辑、 图片编解码优化及资源缓存,DNS 缓存等。

整体优化方法

综上,从一个内容页在列表上的点击,到 WebView 渲染结束,最后到用户的滚动操作,按照时间的顺序,全部的优化策略如下图:

拾遗及 Tips

对于新闻类 App 内容页的完整的解决方案,还有一些基本的技术点,比如模板引擎及模板拼接的模块、JSApi 注入及管理的模块等等,由于篇幅所限,暂且不做深入的展开。

  • 新闻类 App 的内容页,除去基本的渲染 HTML 数据外,同时也需要支持服务于活动、运营的临时 H5 页面。这些页面为了和 Native 进行交互,在自定义 JSApi 注入、JSBridge 的选择、后台下发 domain 黑白名单、以及相关的安全性考虑也是整个实现中重要的一环。同时由于 WKWebView 支持复用回收,加载本地 Html 类型的 WebView 应该与加载 H5 的 WebView 在不同的回收复用池分开管理。

  • 对于内容页图片的管理,绝大多数 App 都将之纳入了 App 统一的图片管理体系中。无论使用哪个开源图片库,在缓存策略上,尽量将内容页图片的缓存策略与其他的有所区分,或者使用 LRU + FIFO 的缓存策略,避免进入内容页大量图片占用缓存空间,导致列表图片释放。同时从使用的角度来说,重复进入同一篇文章的场景也不会频繁的出现。

  • 由于各个 App 的数据接口和技术选型不同,在 HybridPageKit 中只简单的实现了基于 Mustache 的模板拼接,主要是由于它的 logic-less、多终端集成的方便以及开源社区的活跃。对于这部分逻辑,需要根据后台数据的格式及业务需求自定义的扩展。

内容页整体的实现和优化,依赖整个 App 的技术实现和结构,在实现和优化的过程中,还有许多权衡和妥协,以及许多通用的、细节的优化,这里就不一一赘述。

写在最后

文章全部的探索及分析的实现,除对应业务逻辑外,应用封装成三个框架:HybridPageKit、ReusableNestingScrollview 以及 WKWebViewExtension。最终可以通过几十行代码,完成新闻类 App 多种形式的、高性能、易扩展、组件化的内容页实现。

有任何疑问,欢迎提交 issue, 或者直接修改提交 PR!

  作者 GitHub 

https://github.com/dequan1331/HybridPageKit

 
前端之巅 更多文章 我从React身上得到的那些经验教训 引擎V8推出“并发标记”,可节省60%-70%的GC时间 我从两年的JavaScript函数式编程中学到了什么? 你不是一个前端 为什么前端开发这么不稳定?
猜您喜欢 各编程语言开发者最常使用的按键及常用快捷键 大咖来了 | 给成长中的大数据人才最真切最实用的忠告 那些生僻的开源库一 Android内存泄漏分析 聊聊WannaCry电脑勒索病毒