基于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 相关文章推荐
使用forever管理nodejs应用教程
Jun 03 NodeJs
轻松创建nodejs服务器(9):实现非阻塞操作
Dec 18 NodeJs
NodeJS学习笔记之(Url,QueryString,Path)模块
Jan 13 NodeJs
nodejs实现获取某宝商品分类
May 28 NodeJs
使用nodejs开发cli项目实例
Jun 03 NodeJs
使用nodejs中httpProxy代理时候出现404异常的解决方法
Aug 15 NodeJs
详解nodeJS中读写文件方法的区别
Mar 06 NodeJs
nodejs mysql 实现分页的方法
Jun 06 NodeJs
Nodejs实现文件上传的示例代码
Sep 26 NodeJs
通过nodejs 服务器读取HTML文件渲染到页面的方法
May 17 NodeJs
Nodejs使用Mongodb存储与提供后端CRD服务详解
Sep 04 NodeJs
分享node.js实现简单登录注册的具体代码
Apr 26 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
PHP新手上路(八)
2006/10/09 PHP
简单谈谈PHP面向对象之标识对象
2017/06/27 PHP
PHP PDOStatement::bindColumn讲解
2019/01/30 PHP
laravel框架中视图的基本使用方法分析
2019/11/23 PHP
javascript解决innerText浏览器兼容问题思路代码
2013/05/17 Javascript
jquery DIV撑大让滚动条滚到最底部代码
2013/06/06 Javascript
基于jquery实现鼠标滚轮驱动的图片切换效果
2015/10/26 Javascript
JavaScript统计字符串中每个字符出现次数完整实例
2016/01/28 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
原生js实现轮播图的示例代码
2017/02/20 Javascript
妙用缓存调用链实现JS方法的重载
2018/04/30 Javascript
详解通过源码解析Node.js中cluster模块的主要功能实现
2018/05/16 Javascript
学习React中ref的两个demo示例
2018/08/14 Javascript
JS+HTML5 Canvas实现简单的写字板功能示例
2018/08/30 Javascript
微信小程序时间选择插件使用详解
2018/12/28 Javascript
vue中$refs, $emit, $on, $once, $off的使用详解
2019/05/26 Javascript
JS面向对象编程基础篇(二) 封装操作实例详解
2020/03/03 Javascript
vscode+gulp轻松开发小程序的完整步骤
2020/10/18 Javascript
解析Python中的变量、引用、拷贝和作用域的问题
2015/04/07 Python
python实现向ppt文件里插入新幻灯片页面的方法
2015/04/28 Python
为Python的Tornado框架配置使用Jinja2模板引擎的方法
2016/06/30 Python
Python中矩阵库Numpy基本操作详解
2017/11/21 Python
详解Python:面向对象编程
2019/04/10 Python
python绘制已知点的坐标的直线实例
2019/07/04 Python
在django admin中添加自定义视图的例子
2019/07/26 Python
传统HTML页面实现模块化加载的方法
2018/10/15 HTML / CSS
HTML5 canvas基本绘图之绘制曲线
2016/06/27 HTML / CSS
春节超市活动方案
2014/08/14 职场文书
学校领导班子四风问题整改意见
2014/10/02 职场文书
市场调研项目授权委托书范本
2014/10/04 职场文书
申报材料格式
2014/12/30 职场文书
技术员个人工作总结
2015/03/03 职场文书
2019学校运动会开幕词
2019/05/13 职场文书
python爬取新闻门户网站的示例
2021/04/25 Python
Python echarts实现数据可视化实例详解
2022/03/03 Python
【海涛教你打DOTA】死灵飞龙第一视角解说
2022/04/01 DOTA