基于NodeJS的前后端分离的思考与实践(五)多终端适配


Posted in NodeJs onSeptember 26, 2014

前言

近年来各站点基于 Web 的多终端适配进行得如火如荼,行业间也发展出依赖各种技术的解决方案。有如基于浏览器原生 CSS3 Media Query 的响应式设计、基于云端智能重排的「云适配」方案等。本文则主要探讨在前后端分离基础下的多终端适配方案。

关于前后端分离

关于前后端分离的方案,在《基于NodeJS的前后端分离的思考与实践(一)》中有非常清晰的解释。我们在服务端接口和浏览器之间引入 NodeJS 作为渲染层,因为 NodeJS 层彻底与数据抽离,同时无需关心大量的业务逻辑,所以十分适合在这一层进行多终端的适配工作。

UA 探测

进行多终端适配首先要解决的是 UA 探测问题,对于一个过来的请求,我们需要知道这个设备的类型才能针对对它输出对应的内容。现在市面上已经有非常成熟的兼容大量设备的 User Agent 特征库和探测工具,这里有 Mozilla 整理的一个列表。其中,既有运行在浏览器端的,也有运行在服务端代码层的,甚至有些工具提供了 Nginx/Apache 的模块,负责解析每个请求的 UA 信息。

实际上我们推荐最后一种方式。基于前后端分离的方案决定了 UA 探测只能运行在服务器端,但如果把探测的代码和特征库耦合在业务代码里并不是一个足够友好的方案。我们把这个行为再往前挪,挂在 Nginx/Apache 上,它们负责解析每个请求的 UA 信息,再通过如 HTTP Header 的方式传递给业务代码。

这样做有几点好处:

我们的代码里面无需再去关注 UA 如何解析,直接从上层取出解析后的信息即可。如果在同一台服务器上有多个应用,则能够共同使用同一个 Nginx 解析后的 UA 信息,节省了不同应用间的解析损耗。

基于NodeJS的前后端分离的思考与实践(五)多终端适配

来自天猫分享的基于 Nginx 的 UA 探测方案

淘宝的 Tengine Web 服务器也提供了类似的模块 ngx_http_user_agent_module。

值得一提的是,选用 UA 探测工具时必须要考虑特征库的可维护性,因为市面上新增的设备类型越来越多,每个设备都会有独立的 User Agent,所以该特征库必须提供良好的更新和维护策略,以适应不断变化的设备。

建立在 MVC 模式中的几种适配方案

取得 UA 信息后,我们就要考虑如果根据指定的 UA 进行终端适配了。即使在 NodeJS 层,虽然没有了大部分的业务逻辑,但我们依然把内部区分为 Model / Controller / View 三个模型。

基于NodeJS的前后端分离的思考与实践(五)多终端适配

我们先利用上面的图,去解析一些已有的多终端适配方案。

建立在 Controller 上的适配方案

基于NodeJS的前后端分离的思考与实践(五)多终端适配

这种方案应该是最简单粗暴的处理方法。通过路由(Router)将相同的 URL 统一传递到同一个控制层(Controller)。控制层再通过 UA 信息将数据和模型(Model)逻辑派发到对应的展现(View)进行渲染,渲染层则按预先的约定提供了适配几个终端的模板。

这种方案的好处是,保持了数据和控制层的统一性,业务逻辑只需处理一次遍可以应用在所有终端上。但这种场景只适合如展示型页面等低交互型的应用,一旦业务比较复杂,各个终端的 Controller 可能有各自的处理逻辑,如果还是共用一个 Controller ,会导致 Controller 非常的臃肿而且难以维护,这无疑是一个错误的选择。

建立在 Router 上的适配方案

为了解决上面遇到的问题,我们可以在 Router 上就将设备区分,针对不同的终端分发到不同的 Controller 上:

基于NodeJS的前后端分离的思考与实践(五)多终端适配

这也是最常见的方案之一,大多表现在针对不同终端使用各自独立的一套应用。如 PC 淘宝首页和 WAP 版的淘宝首页,不同设备访问 www.taobao.com ,服务器会通过 Router 的控制,重定向到 WAP 版的淘宝首页或者 PC 版的淘宝首页,它们各自是完全独立的两套应用。

但这种方案无疑带来了数据和部分逻辑无法共用的问题,各种终端之间无法分享同一份数据和业务逻辑,产生大量重复性工作,效率低下。

为了缓解这个问题,有人提出了优化后的方案:依然是在同一套应用里面,各个数据来源抽象成各个 Model,提供给不同终端的 Controller 组合使用:

基于NodeJS的前后端分离的思考与实践(五)多终端适配

这个方案解决了前面数据无法共用的问题。在 Controller 上各个终端还是相互独立,但能共同使用同一批数据源,至少在数据上无需再针对终端类型开发独立的接口了。

以上两种基于 Router 的方案,由于 Controller 的独立,各个终端可以为自己的页面实现不同的交互逻辑,保证了各终端自身足够的灵活度,这也是为什么大部分应用采用这种方案的主要原因。

建立在 View 层的适配方案

这是淘宝下单页面使用的方案,不过区别是下单页将整体的渲染层放在了浏览器端,而不是 NodeJS 层。不过无论是浏览器还是 NodeJS,整体设计思路还是一致的:

基于NodeJS的前后端分离的思考与实践(五)多终端适配

在这个方案里面,Router、Controller 和 Model 都无需关注设备信息,终端类型的判断完全交给展现层来处理。图中主要的模块是「View Factory」,Model 和 Controller 将数据和渲染逻辑传递过来之后,通过 View Factory 根据设备信息和其它状态(不仅仅是 UA 信息、也可以是网络环境、用户地区等等)从一堆预设好的组件(View Component)中抓取特定的组件,再组合成最终的页面。

这种方案有几个优势:

上层无需关注设备信息(UA),多终端的视频还是交由和最终展现最大关系的 View 层来处理;不仅仅是多终端适配,除了 UA 信息,各个 View Component 还可以根据用户状态决定自身输出何种模版,如低网速下默认隐藏图片、指定地区输出活动 Banner。每个 View Component 的不同模版间可以自行决定是否使用同一份数据、业务逻辑,提供十分灵活的实现方式。

但明显的是,这个方案也是最复杂的,尤其是要考虑一些富交互的应用场景时,Router 和 Controller 也许无法保持这么纯粹。特别对于一些整体性比较强的业务,本身无法被拆分成组件,这种方案也许并不适用;而且对于一些简单的业务,使用这种架构可能不是最佳的选择。

总结

以上几种方案,都各自体现在 MVC 模型中的一个或多个部分,在业务上如果一个方案不满足需求,更可以采取多个方案同时采用的方式。或是可以理解为,业务上的复杂度和交互属性决定了该产品更适合采用哪种多终端适配方案。

对比基于浏览器的响应式设计方案,因为绝大部分终端探测和渲染逻辑迁移到了服务端,所以在 NodeJS 层进行适配无疑带来了更好的性能和用户体验;另外,相对于一些所谓的「云适配」方案带来的转换质量问题,在基于前后端分离的「定制式」方案中也不会存在。前后端分离的适配方案在这些方面有着天然优势。

最后,为了适应更灵活的强大的适配需求,基于前后端分离的适配方案将会面临更多挑战!

NodeJs 相关文章推荐
NodeJS学习笔记之网络编程
Aug 03 NodeJs
Nodejs实现的一个静态服务器实例
Dec 06 NodeJs
使用nodejs中httpProxy代理时候出现404异常的解决方法
Aug 15 NodeJs
NodeJS收发GET和POST请求的示例代码
Aug 25 NodeJs
nodejs操作mongodb的填删改查模块的制作及引入实例
Jan 02 NodeJs
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
Jan 05 NodeJs
nodejs前端模板引擎swig入门详解
May 15 NodeJs
nodejs 十六进制字符串型数据与btye型数据相互转换
Jul 30 NodeJs
NodeJs项目中关闭ESLint的方法
Aug 09 NodeJs
Nodejs核心模块之net和http的使用详解
Apr 02 NodeJs
搭建一个nodejs脚手架的方法步骤
Jun 28 NodeJs
node快速搭建后台的实现步骤
Feb 18 NodeJs
基于NodeJS的前后端分离的思考与实践(四)安全问题解决方案
Sep 26 #NodeJs
基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架
Sep 26 #NodeJs
基于NodeJS的前后端分离的思考与实践(二)模版探索
Sep 26 #NodeJs
基于NodeJS的前后端分离的思考与实践(一)全栈式开发
Sep 26 #NodeJs
Nodejs Post请求报socket hang up错误的解决办法
Sep 25 #NodeJs
Nodejs实现的一个简单udp广播服务器、客户端
Sep 25 #NodeJs
Nodejs异步回调的优雅处理方法
Sep 25 #NodeJs
You might like
ThinkPHP框架实现session跨域问题的解决方法
2014/07/01 PHP
PHP清除字符串中所有无用标签的方法
2014/12/01 PHP
js 分栏效果实现代码
2009/08/29 Javascript
理解Javascript_01_理解内存分配原理分析
2010/10/11 Javascript
jQuery动态创建html元素的常用方法汇总
2014/09/05 Javascript
JavaScript动态创建link标签到head里的方法
2014/12/22 Javascript
JavaScript设置获取和设置属性的方法
2015/03/04 Javascript
基于jquery实现的自动补全功能
2015/03/12 Javascript
js限制文本框只能输入整数或者带小数点的数字
2015/04/27 Javascript
js实现仿京东2级菜单效果(带延时功能)
2015/08/27 Javascript
jQuery ajax调用后台aspx后台文件的两种常见方法(不是ashx)
2016/06/28 Javascript
Angular2数据绑定详解
2017/04/18 Javascript
基于axios封装fetch方法及调用实例
2018/02/05 Javascript
vue组件传递对象中实现单向绑定的示例
2018/02/28 Javascript
JS中用EL表达式获取上下文参数值的方法
2018/03/28 Javascript
详解ES6中的 Set Map 数据结构学习总结
2018/11/06 Javascript
监听angularJs列表数据是否渲染完毕的方法示例
2018/11/07 Javascript
微信小程序出现wx.getLocation再次授权问题的解决方法分析
2019/01/16 Javascript
javascript+HTML5 canvas绘制时钟功能示例
2019/05/15 Javascript
jquery实现垂直无限轮播的方法分析
2019/07/16 jQuery
使用Node.js在深度学习中做图片预处理的方法
2019/09/18 Javascript
Vue + ts实现轮播插件的示例
2020/11/10 Javascript
caffe binaryproto 与 npy相互转换的实例讲解
2018/07/09 Python
用Python实现筛选文件脚本的方法
2018/10/27 Python
Python学习笔记之pandas索引列、过滤、分组、求和功能示例
2019/06/03 Python
Python爬虫图片懒加载技术 selenium和PhantomJS解析
2019/09/18 Python
python脚本实现音频m4a格式转成MP3格式的实例代码
2019/10/09 Python
如何解决tensorflow恢复模型的特定值时出错
2020/02/06 Python
Python偏函数Partial function使用方法实例详解
2020/06/17 Python
化石印度尼西亚在线商店:Fossil Indonesia
2019/03/11 全球购物
《开国大典》教学反思
2014/04/19 职场文书
2014年社区工会工作总结
2014/12/18 职场文书
房产证明范本
2015/06/19 职场文书
生日赠语
2015/06/23 职场文书
立案决定书范文
2015/06/24 职场文书
校园运动会广播稿
2015/08/19 职场文书