今天我们来聊一聊直播间 Web App 设计技术。在实际开发过程中,你可能经常会遇到这种尴尬场景,一个功能,需要你三端都要实现一遍,这样很容易影响到功能迭代。面对这样的问题,我们选择使用 Web App 技术来解决我们的困扰。你可能听起来有些疑惑,Web App 为什么可以解决这样的问题?
那么首先我们就一起来看一下,Web App 究竟是什么,它是如何运行的?为什么我说这种场景下 Web App 是更好的选择?
如何顺利运行 Web App?
实际上,Web App 是一种使用 HTML5 编写的,运行在 PC、Android、iOS 等平台上的 Web 应用,这种 Web 应用想要运行在各种平台的客户端上,需要依赖客户端的容器层以及一定的通信能力。
我将会分两步来帮助你理解什么是容器层,以及有了容器之后,我们的客户端又是如何凭借容器来和容器内部的 Web 应用进行通信的。
容器层
那么首先我们就先来了解什么是容器层,顾名思义,容器就是指 Native 运行 Web App 的容器。这个容器一般使用 WebView 组件,或者 WKWebView 来实现。
既然有了容器,为何还会有一个容器层的概念呢?这是因为,在实际工作中,除了加载 Web 页面这样的基础能力以外,客户端还需要很多额外的能力来辅助这个容器,所以各端需要各自实现一套容器层逻辑,来保证容器的完整性和可用性。
容器层的作用一共有三种:
- 文件资源加载和解压的能力:为容器内 Web App 提供所需要的在线资源。
- 生命周期管理能力:便于在复杂的业务场景中更好地管理各种 Web App。
- 和 Web 的通信能力:使 Native 层能和容器内部的 Web App 进行相互通信。
可以看到,容器的职责还是比较明确的,总结来说就是:
当需要时,Native 创建容器,并且下载解压 Web App 网页及相关资源到本地,挂载相应的 HTML 页面到容器上;而当运行结束后,Native 会销毁容器并释放资源。

通信策略
通过了解容器层,我们知道了,Native 和 Web App 需要通过容器层进行通信,那么,你知道有什么场景需要 Native 和 Web App 的通信吗?下面我就来列举几个场景吧,比如:Native 和 Web 之间的数据共享、进行超越 Web App 自身生命周期的数据存储、借用客户端的长连接以及调用原生能力等等。这些 Web App 自身无法覆盖的场景,便需要 Native 辅助实现。
具体实现上,各端需要各自实现一套自己的 JSBridge,各自通过 JSBridge 实现 Native 和 Web App 的通信。
通常做法是向 Window 对象上注入方法,来实现互相调用及数据传输。数据的传输格式可以是二进制,也可以是 JSON 字符串。H5 调用 Native 时,原生将 WebView JavaScript Bridge 绑定在 Window 上,H5 直接调用这个对象中的原生接收方法。Native 调用 H5 时,H5 将 JSBridge 绑定在 Window 上,Native 通过原生方法调用这个对象上的 H5 接收方法。

有了容器,有了通信机制,Web App 运行的基础便已经搭建完毕。现在的你是否能理解为什么要使用 Web App 了呢?
事实上,相较于 Native App,Web App 的优点显而易见:
- 一套 Web 代码可以运行在多端,极大地减少了开发量,提升业务迭代效率;
- Web App 页面的资源,比如 HTML、JS、CSS,都可以通过访问时即时下载的方式去在线获取,因此,Web App 独立发布和更新,就无需依赖客户端发版周期和审核周期,更新迭代会更加迅速。
在直播间业务场景的逐渐丰富的当下,Web App 更新速度迭代快、拥有独立更新发版的能力,很好地满足了当下直播业务的需求。
继续回到我们的场景下,在直播课堂内,该怎么使用 Web App 来实现我们的业务呢?Native 又该怎么去加载 Web App 呢?
直播间 WebApp 加载策略
我们知道,在直播课堂中,一堂课里会有很多页课件,不同的课程使用的课件各不相同。这些课件的加载方式是有一套固定的规则的。同理,直播间内的广告位,分享时的弹出页,以及各种礼物动效等等,这些有其特有加载规则的直播间内容,就可以搞成各种 Web App 应用,不同的只是加载的配置和资源不同。

在此基础上,我们看下直播间内 Web App 加载的基本流程:
- 每一种直播间内活动都对应一种 Web App,比如广告位、分享页、礼物、购物车等等。
- 每一种 Web App 应用将资源打包出来,以 zip 形式上传到 CDN 上,客户端使用时下载当前活动对应的资源,并解压挂载到 WebView 上。这些 zip 我们可以称之为 webAppBundle。
- 除此之外,即使同是广告,每两个直播间的广告之间也是不同的,Web App 所加载的内容决定了相同 Web App 的实例差异性,这里包含了整个活动 Web App 的相关配置以及相关图片、字体资源等。这些内容同样以 zip 的形式上传到 CDN 上,我们可以称之为 instanceBundle,这里我建议可以搞一个专门生产 Web App 资源的编辑平台,由运营人员更方便地生产各个直播间内所需要展现的资源。
- 另外,一个直播间内需要有哪些活动,哪些是旧版本哪些是新版本,类似这种信息我们需要在直播间初始化的时候就获取到,并且选择性地加载相应的 Web App。这些直播间内的全量 Web App 信息我们可以称为 webAppInfo。
这样下来,整个 Web App 加载流程的初步结构就很清晰了,下面用一张图来简单表示一下:

现在这样,我们已经实现了一套简单的直播间 Web App 架构。下面我们可以在此基础上继续优化这个架构,让它变得更加酷炫和灵活。
Web App 和微前端技术的结合
先来看看上面这种模式的缺点。试想一下,如果每一个 Web App 都需要挂载到一个 WebView 实例,那么对于一个生命周期内多个 Web App 共存的场景,比如一个直播间内,上面提及的广告位、礼物特效、购物车页面等等,都是在一场直播内实际存在的,这样下来客户端性能显然是吃不消的。
那么是否有让多个 Web App 共存在一个 WebView 实例内的实现方法呢?答案是肯定的。
在这里,要提到一个新的概念: 微前端。
简单来说,微前端就是将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品。
那么来看一下我们的业务场景------直播间内的各种活动,恰恰就是各种更小、更独立的模块。我们希望它们可以独立开发、测试、部署,又不希望它们分散成多个 Web App 来运行。那么我们就可以借助微前端的方案,让多个应用在 Web App 层面内作为一个整体来运作,但是在组装上又可以自由组合、拆分各个模块。
然而我们知道,多个 Web 应用自己肯定是没有办法组装成一个整体运行在一个 WebView 内的,它们每一个个体都应该是一个非必要的、可拆卸的 Web 应用。那么要实现这种微前端的方式,这里就不得不引入一个大厅 (runtimeLib) 的概念,在微前端中更多地被称为主应用。
在这里,简单向你介绍一下大厅的基本结构和各结构的功能:
- 主工程基座用于加载子应用列表、注册子应用、管理子应用公共依赖。
- 应用管理用于异步加载子应用,编译导出内容;路由劫持,跳转子应用;子应用通信,数据共享。
- 沙盒子隔离子应用用于子应用间减少相互副作用、样式影响、事件影响、全局变量影响。
大厅作为一个 Web 应用,同时也是直播间内 Web App 的必须依赖项。从结构中可以看出,搭载了微前端框架的它具有根据直播间特性选择性加载子应用的能力,并且具有一定程度的应用管理和隔离子应用的能力。

凭借着微前端的技术能力,我们就可以实现同一生命周期内的所有 Web App 只加载在一个 WebView 内,所有 Web App 的公共依赖只加载一次,同时可以更加灵活地控制需要加载和卸载的 Web App。

这样,原来的加载模式就由上图变成下面这张图的模式了:

如此,Native 和 Web App,以及各 Web App 相互之间,就实现了各自维护、独立发版的解耦状态。既是独立的个体,又能在直播间的生命周期内,组成一个和谐而统一的整体。
当然,各子应用之间会有业务关联和依赖,这是不可避免的。所以,为了保证各 Web App 完全独立,避免直接耦合,各自应用之间需要通过自定义事件进行间接通信。但原则上,应该尽可能减少子应用间的通信,以避免大量弱依赖造成的强耦合。比较通用的逻辑和数据可以直接在大厅实现,这样也可以减少一定的通信数量。
这样,一套完整的 Web App 运行时的模型就搭建完毕了。另外,Web App 作为独立于客户端的存在,其相关的基础设施建设也是必不可少的。这也是一个长期的工作,可以在之后业务的开展中持续优化和迭代。
总结
今天的分享到这里就结束了,下面我来做一个总结,今天主要分享的内容是直播间内的 Web App 技术方案,有几个重点希望你记住。
- 我们之所以使用 Web App,是因为它确实可以大大减少一个产品的研发周期,并且减少其维护和迭代成本。
- Web App 的运行依赖于 Native 的容器,Web App 的通信需要各 Native 端和 Web 本身实现一套通信的 Bridge,根据这层 Bridge 以及自定义数据协议来进行通信,以便于辅助 Web App 实现更复杂的应用场景。
- 至于 Web App 的加载,在单个 Web App 自成一个独立应用的基础上,我们借助微前端的方案,让多个 Web App 成为一个整体运行在 WebView 上成为了现实,大大降低了客户端的性能损耗,同时让直播间内的各个活动的维护和开发变得轻量、简洁、更易维护。