序
近年来,各站点基于Web的多终端适配如火如荼,各行业间纷纷研发出依托各种技术的解决方案。如基于浏览器原生CSS3 Media Query的响应设计、基于云智能重排的云适配方案等。本文主要讨论基于前端分离的多终端适配方案。
关于前后端的分离
《基于NodeJS的前后端分离的思考与实践(一)》中明确说明了前后端分离的方案。我们引入NodeJS作为服务器接口和浏览器之间的渲染层。由于NodeJS层与数据完全分离,不需要关心大量的业务逻辑,非常适合在这个层进行多终端适配。
普遍获得检测
多终端适配首先要解决的就是UA检测。对于传入的请求,我们需要知道该设备的类型,然后才能为其输出相应的内容。目前市面上已经有非常成熟的与大量设备兼容的User Agent特征库和检测工具,下面是Mozilla整理的列表。其中有些工具运行在浏览器端和服务器端代码层,甚至有些工具提供了Nginx/Apache模块,负责解析每个请求的UA信息。
其实我们推荐最后一种方式。基于前端分离的方案决定了UA检测只能在服务器端运行,但是将业务代码中的检测代码和特征库进行耦合,并不是一个足够友好的方案。我们将这一行为向前推进,并将其挂在Nginx/Apache上。他们负责解析每个请求的UA信息,然后通过HTTP Header传递给业务代码。
这样做有几个好处:
在我们的代码中,不需要关注UA是如何解析的,可以直接从上层取出解析出来的信息。如果同一个服务器上有多个应用,那么同一个Nginx解析的UA信息可以一起使用,节省了不同应用之间的解析损失。
天猫共享的基于Nginx的UA检测方案
淘宝的Tengine Web服务器也提供了类似的模块ngx_http_user_agent_module。
值得一提的是,在选择UA检测工具时,必须考虑特征库的可维护性。由于市场上的新型设备越来越多,每个设备都会有自己的User Agent,因此特征库必须提供良好的更新和维护策略,以适应不断变化的设备。
基于MVC模式的几种适配方案
获取UA信息后,要根据指定的UA考虑终端适配。即使在NodeJS层,虽然没有业务逻辑,但我们仍然将内部分为三个模型:模型/控制器/视图。
首先,我们使用上图来分析一些现有的多终端适配方案。
基于控制器的自适应方案
这个方案应该是最简单粗暴的处理方式。通过路由器将同一网址传递到同一控制层(控制器)。控制层通过UA信息将数据和模型逻辑分发到相应的View进行渲染,而渲染层则根据预协议提供适合多个终端的模板。
该方案的优点是保持了数据层和控制层的统一,业务逻辑可以一劳永逸地应用于所有终端。但是,这种场景只适用于显示页面等低交互应用。一旦业务变得复杂,每个终端的控制器可能都有自己的处理逻辑。如果他们还是共用一个Controller,会导致控制器非常臃肿,难以维护,这无疑是一个错误的选择。
基于路由器的自适应方案
为了解决上述问题,我们可以区分路由器上的设备,并将其分配给不同终端的不同控制器:
这也是最常见的方案之一,主要表现在为不同的终端使用一组独立的应用程序。比如PC淘宝主页和WAP淘宝主页,当不同设备访问www.taobao.com时,服务器会在Router的控制下重定向到WAP淘宝主页或PC淘宝主页,这是两个完全独立的应用。
但这种方案无疑带来了数据和部分逻辑无法共享的问题,各种终端无法共享相同的数据和业务逻辑,导致大量重复工作,效率低下。
为了缓解这个问题,有人提出了一个优化方案:仍然在同一个应用中,把每个数据源抽象成每个Model,提供给不同终端的Controller组合使用;
该方案解决了以往数据无法共享的问题。在控制器上,所有终端彼此独立,但它们可以一起使用同一批数据源。至少,不需要为终端类型开发独立的接口。
在以上两种基于路由器的方案中,由于Controller的独立性,每个终端可以为自己的页面实现不同的交互逻辑,保证了每个终端足够的灵活性,这也是大多数应用采用这种方案的主要原因。
基于视图层的自适应方案
这是淘宝点餐页面使用的方案,不同的是整个渲染层放在浏览器端,而不是NodeJS层。但是,无论是浏览器还是NodeJS,整体的设计思路都是一样的:
该方案中,路由器、控制器、模型不需要关注设备信息,终端类型的判断完全交给表示层。图中的主要模块是“查看工厂”。在模型和控制器传输数据和渲染逻辑后,根据设备信息和其他状态(不仅是UA信息,还有网络环境、用户区域等)从一堆预设组件(View Component)中抓取特定组件。)通过视图工厂,然后组合成最终页面。
这个方案有几个优点:
上层无需关注设备信息(UA),多终端视频仍由最终呈现最大关系的View层处理;不仅是多终端适配,还有UA信息,每个View Component都可以根据用户状态决定输出哪个模板,比如在网速较低时默认隐藏图片,在指定区域输出活动Banner。每个视图组件的不同模板可以自行决定是否使用相同的数据和业务逻辑,提供了非常灵活的实现模式。
但显然,这个方案也是最复杂的,尤其是在考虑一些交互应用场景时,Router和Controller可能就没有那么纯粹了。特别是对于一些完整性强的业务,不能拆分成组件,所以这个方案可能不适用;对于一些简单的服务,使用这种架构可能不是最好的选择。
摘要
上述每个方案都包含在MVC模型的一个或多个部分中。在业务上,如果一个方案不能满足需求,可以同时采用多个方案。或者可以理解,服务的复杂性和交互属性决定了产品更适合哪种多终端适配方案。
相比基于浏览器的响应式设计方案,由于终端检测和渲染逻辑大部分迁移到了服务器端,NodeJS层的适配无疑带来了更好的性能和用户体验;此外,与一些所谓的“云适配”方案相比,基于前端分离的“定制化”方案不会存在转换质量问题。前端分离的适配方案在这些方面有着天然的优势。
最后,为了适应更灵活、更强大的适配需求,基于前端分离的适配方案将面临更多挑战!