月活跃用户达5.16亿的微博是如何实现跨平台稳定开发、快速迭代的?

2022-03-31 14:29:08 浏览数 (1)

微博作为一款体量巨大的应用,能够快速高效的在多个平台上实现复杂的业务功能,是它成功的重要因素之一。在不断前行的路上,微博有哪些成功经验可以供广大开发者借鉴与学习?

本文根据微博客户端移动技术专家程伟在11月19日软件绿色联盟开发者大会发表的《微博跨平台开发实践之路》主题演讲整理而成,从技术方案、跨平台组件、微博Card、微博小程序四个方面分享了微博客户端开发过程中遇到的问题及改进方式

微博H5 Hybrid应用

H5 Hybrid作为老牌的跨平台方案,已被广大开发者所熟悉,因此开发者上手也相对比较容易,微博有大量业务也是基于这种方案实现的。H5 Hybrid方案基于各个操作系统对浏览器的内置实现了跨平台,但同时也继承了浏览器交互性能不足、渲染性能差,以及基础能力弱的弊端。而这些问题在原生开发中已经有了非常成熟的解决办法。因此微博要做的就是将原生的能力提供给浏览器,让H5 Hybrid方案更加强大。

1.1 H5 Hybrid - 技术方案

微博采用的是主流的JS Bridge方案,借助于浏览器中的JS引擎打通H5和Native的通信能力,同时还基于一些业务需求实现了权限控制模块,用于针对一些敏感的API进行调用管控。另外对于一些热门的应用场景,还提供了离线资源缓存的方案,用来解决H5加载慢的问题。

1.2 H5 Hybrid - 应用场景

H5 Hybrid方案在微博中应用广泛,如熊猫吃竹子的分享、内置浏览器查看大图等功能,同时由于它灵活、便捷且支持动态发布的特点,也成了运营、推广类业务的首选方案。但是Hybrid方案并不适用一些性能要求较高的场景,对于这类场景可以使用使用C 跨平台方案。

C 跨平台组件

与H5基于浏览器实现跨平台不同,C 跨平台的基础来自于操作系统对这门语言的原生支持。正是得益于这一点,可以在C 方案里面使用非常底层的系统能力,如socket、mmap等API,从而获得强大的能力和高超的性能。微博基于这一方案实现了统一的网络引擎、通用日志传输与存储模块等功能组件。同时,为了方便C 开发的组件比较容易接入到原生开发中,微博还在双端实现了通用的适配层,解决了数据格式转化和数据传输等问题。

从目前实现的几个案例里面大家可以发现,这个方案适用于高性能和安全线要求较高的基础通用功能场景,如果涉及到界面、用户交互的场景则不再适用,这种场景就要使用Card方案了。

微博 Card方案

Card,顾名思义是卡片。把一个复杂的功能页面拆分成多行,每一行就是一个具有独特形态的卡片。

3.1 微博 Card – 技术方案

在Card方案中,通过一个复杂的Json数据结构来描述一个页面。页面数据结构中最重要的是“Cards”数组,数组中的每一项数据则描述了每张卡片需要的具体数据,这些数据可以通过“Card_type”属性标识它所需要的卡片类型。

每一种类型的卡片,都有其特定的样式、交互方式以及支持的业务能力,微博通过分析整理各方的业务需求,已经实现了100多种形态各异的卡片。业务方可以通过组织Cards数据,从这些卡片中灵活选用自己想要的样式,自由拼装出想要实现的业务。微博已经在iOS、安卓、H5上都实现了这100多种卡片,同一个Json数据源可以在三端得到一致的功能页面。

3.2 微博 Card - 应用场景

Card方案在微博有很多应用场景,比如个人主页、微博搜索结果页等。Card方案基于纯原生的实现,拥有很多原生开发的特色能力,对于强信息展示的场景有较强的优势。但同时,Card方案不支持定制的缺点也很明显,每一种卡片从创建之初所支持的样式、交互形式以及业务逻辑已经被固定下来,且不支持修改,如果想要修改卡片行为,就需要创建一种新的卡片。也就是说,Card方案在信息展示方面有出色的表现,但不适用于经常变更的场景。为了应对场景变更的需求,微博推出了微博小程序方案。

微博小程序

微博小程序从去年8月份开始研发,到今年5月初正式上线第一个业务。作为一个新开发模式,微博小程序的首要目标是在应用商店审核日益严格的背景下,为业务方提供快速上线以及快速变更的能力。

4.1 微博小程序 - 技术方案

微博小程序的整体技术框架从下往上看,主要分为渲染引擎层、容器层、前端框架层、组件&API能力层及IDE五部分。

渲染引擎层:小程序页面使用浏览器进行渲染,iOS使用的是系统的WKWebview,在安卓上使用的是基于Chrome定制的Yttrium 内核;

容器层:在小程序容器层实现了小程序生命周期的管理、小程序包管理以及保活机制等;

前端框架层:这部分主要是小程序开发者编写小程序页面,解决呈现以及交互的问题;

组件&API能力:在这里小程序提供大量的业务组件给开发者使用,将硬件能力、设备能力以及微博的特色业务能力包装成API提供给开发者。

除此以外,微博还打造了小程序IDE,提供从代码编辑、预览、调试以及上传的完整的开发流程。

4.2 微博小程序 - 前端框架

小程序前端框架分为Render层和Service层上下两层,Render层主要用于解决小程序页面渲染以及用户交互问题,可以将其理解成浏览器中的一个网页;下层Service层主要用于执行小程序的业务逻辑,这一层是一个独立的JS引擎,是小程序基础的运行环境。

微博小程序前端使用Vue打造,支持Vue绝大部分语法。开发者所编写的小程序页面就是一个Vue页面,这个页面经过小程序IDE的编译打包变成JS,这是一个纯粹的前端构建过程,基于Vue-loader 、Webpack完成的。JS文件中包含VDOM和JS代码两个部分。VDOM是最终页面结构的虚拟描述,包括页面如何布局及如何应用CSS样式;JS代码包括小程序开发者所编写的相关事件处理函数以及对应的业务逻辑代码。

JS文件会加载到Service层的JS引擎中,同时加载进来的还有Vue框架的基础库。当用户点击小程序页面后,Vue框架会解析对应的VDOM结构,这个过程中会产生大量的渲染指令,这些指令在浏览器 Document 对象上进行操作,告诉浏览器该如何绘制出页面内容。由于Service层 JS引擎没有浏览器Document对象而不能处理这些指令,需要转发给上层浏览器中的JS引擎。

Service JS引擎如何将指令传递到上层呢?主要是借助于JS Bridge来完成,通过在下层的JS 引擎中注入一个虚拟的Document对象,由JS Bridge拦截虚拟Document对象上所有方法调用,就可以把渲染指令拦截下来并转发给上层。当浏览器JS引擎收到渲染指令后,会转交给浏览器内核,由浏览器内核完成整个页面的渲染。在这个浏览器页面上会产生一些用户交互行为,比如在浏览器框架下点击一个按纽,按钮的onclick事件会触发浏览器JS引擎中的处理函数。同样的道理,可以通过JS Bridge拦截这部分调用,同时转交给下层,这样下层开发者所编写的具体交互事件处理逻辑就被触发了。比如可以在下层JS代码中发起一个网络请求,获取网络数据并设置给页面。在Vue框架下这个数据的变化会导致Vdom变化,而VDOM的变化会产生新的渲染指令,渲染指令会再次被转发到上层,由浏览器重绘这个页面。这样就完成了从页面绘制到用户交互,进而影响这个页面显示的完整过程。

通过上文的描述也可以看到,在Render层和Service层交互的过程中,需要频繁的从JS到Native去传输数据,而这个传输过程需要进行数据的序列化和反序列化,这是一个非常低效的过程,因此引入了JS Binding方案来加速这个过程。

4.2.1 微博小程序 – 同层渲染

微博小程序基于浏览器进行渲染,自然也避免不了浏览器在一些特定场景下交互体验很差的问题,比如像文字输入的场景以及视频播放的场景。结合下方视频案例来进行说明,通过视频可以看到上半部分是H5播放器,下半部分是原生播放器,同样的视频在同样的网络环境下播放,不难发现由于H5播放器加载慢导致整体播放进度落后于原生播放器,而且在原生播放器中还有弹幕及加关注的交互组件,这些都是H5播放器不具备的。因此,在特定场景下将浏览器控件替换成原生控件,可以大幅度提升小程序交互体验。

视频

要如何实现控件替换呢?比较简单的实现方式是直接将原生控件覆盖在浏览器上,只要精准控制它的位置和大小且与浏览器控件保持一致,就可以保证用户看到和与之交互的都是原生控件。但这个方案存在一个弊端,当用户在浏览器页面上滑动时,页面上包含原生控件在内的所有元素都需要随手运动。为了滑动原生控件,要从浏览器中获取用户的滑动速度、滑动位置等数据,同时要将这些数据传输给Native层,由Native层控制原生控件跟随滑动。而数据传输过程需要借助JS引擎,期间必然会产生性能损耗,很可能出现传输延迟问题,这时原生控件就无法与页面整体滑动实现同步,就会出现卡顿现象。

为了真正实现原生控件与页面的滑动同步,最好的方案就是让原生控件与浏览器融为一体,而这就是同层渲染要解决的问题。结合刚才的案例进行说明,页面通过渲染之后,上半部分浏览器的播放器控件和下半部分原生控件都属于小程序页面视图,这样当用户滑动时页面内部所有元素都会一起动,自然解决了滑动同步的问题。

如何实现呢?iOS要通过找到浏览器为特定控件或特定样式预留的坑,然后嵌入原生控件来实现。而安卓体系较复杂,需要通过定制浏览器内核来实现,这里就不在做扩展说明。

4.2.2 微博小程序 – 基于 Card 方案的原生渲染

借助于同层渲染方案,浏览器中的小程序可以获得较好的交互体验,但依然存在浏览器渲染性能差、无法支撑功能复杂的页面的问题,但是上文中提到过,Card方案可以实现微博信息流这样复杂的功能,因此微博提出了将Card方案与小程序方案结合到一起的构想。为了实现这个方案,微博将使用原生的Card页面替换了原来负责页面渲染的浏览器。由于原生Card页面天然支持用户交互,所以整个交互事件的传输通道也很容易建立。这样就得到了一个基于Card的原生渲染小程序。

Card方案与小程序结合后还可以解决Card不支持定制的问题,借助小程序的Service层,开发者所编写的事件处理函数就可以执行任意业务逻辑,也就是说通过小程序方案可以赋予Card完整的业务定制能力。而对于小程序来说,不仅收获了一个原生的渲染页面,还得到了100多种现成的业务组件,这就是这个组合方案的意义。

4.2.3 微博小程序 – 基于 Flutter 的渲染方案(预研)

小程序想进行原生渲染,除了与Card方案结合外,还可以采用系统自带的标准渲染API,或是第三方实现的各类渲染方案,这里主要介绍基于Flutter 的渲染方案。

虽然Flutter可以提供与跨平台一致的渲染能力,但是并不能理解小程序中渲染指令,因此需要有一个模块来解释这些渲染指令,并翻译成Dart可以理解的命令。这里主要是借助可以同时满足跨平台、高性能要求的C 方案,基于C 实现的Layout层专门处理小程序的渲染指令。结合诸多实际开发中遇到的问题,会要求开发者在这个方案下只允许使用标准页面结构开发,并且只能使用一部分经过筛选的CSS的样式,这样就能得到一个相对容易处理的简化的渲染指令。

在渲染过程中Layout模块需要频繁传输大量渲染的命令给Flutter层,由于Flutter采用的Dart语言不能直接和Native进行通信,还需要借助Channel层才能完成交互通道,但Channel的传输效率低,这里与上文提到的JS层与Native层传输问题是一样的,所以可以要借鉴JS Binding的传输思路来提升C 到Data及JS到Data的快速传输通道。

4.3 微博小程序 – 应用场景

微博小程序从上线以来已经支撑了大量的内部业务,在实际应用中基于规避审核风险及扩大内部使用场景的考虑,微博刻意淡化了小程序的UI特征。同时基于业务方需求,进一步改造了小程序框架,支持输出页面级小程序,也就是现在小程序输出不再是一个APP而是Activity页面。这个 Activity页面与其他业务中用到的原生Activity页面是一致的,所有的统一页面跳转、路由、记时规则都能继续使用。为了进一步扩大小程序的适用范围,现在还支持输出视图级小程序。小程序支持以任意大小展现在任何页面的任何位置,并且还能跟页面其他元素进行交互。

微博小程序使用前端技术体系打造,具备H5灵活快速的特点。同时小程序前端采用渲染和逻辑分离方法进行设计,不仅可以直接使用系统的大量API提升小程序的性能,还可以直接采用原生渲染能力提升小程序的体验。

END

0 人点赞