基于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框架Express的模板视图机制分析
Jul 19 NodeJs
Windows系统中安装nodejs图文教程
Feb 28 NodeJs
Nodejs学习笔记之入门篇
Apr 16 NodeJs
浅谈Nodejs中的作用域问题
Dec 26 NodeJs
nodejs开发——express路由与中间件
Mar 24 NodeJs
nodejs学习笔记之路由
Mar 27 NodeJs
详解nodejs微信公众号开发——2.自动回复
Apr 10 NodeJs
nodejs爬虫初试superagent和cheerio
Mar 05 NodeJs
nodejs中express入门和基础知识点学习
Sep 13 NodeJs
nodejs 使用 js 模块的方法实例详解
Dec 04 NodeJs
PHPStorm中如何对nodejs项目进行单元测试详解
Feb 28 NodeJs
使用nodejs分离html文件里的js和css详解
Apr 12 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+Ajax检测用户名或邮件注册时是否已经存在实例教程
2014/08/23 PHP
PHP的几个常用加密函数
2016/02/03 PHP
Zend Framework教程之Zend_Registry对象用法分析
2016/03/22 PHP
php简单复制文件的方法
2016/05/09 PHP
PHP对象链式操作实现原理分析
2016/10/09 PHP
ThinkPHP框架整合微信支付之Native 扫码支付模式二图文详解
2019/04/09 PHP
php判断目录存在的简单方法
2019/09/26 PHP
Javascript 中文字符串处理额外注意事项
2009/11/15 Javascript
通过JavaScript使Div居中并随网页大小改变而改变
2013/06/24 Javascript
从jquery的过滤器.filter()方法想到的
2013/09/29 Javascript
在jquery中的ajax方法怎样通过JSONP进行远程调用
2014/04/04 Javascript
jQuery 重复加载错误以及修复方法
2014/12/16 Javascript
JavaScript简单下拉菜单实例代码
2015/09/07 Javascript
Bootstrap滚动监听组件scrollspy.js使用方法详解
2017/07/20 Javascript
快速解决layui弹窗按enter键不停弹窗的问题
2019/09/18 Javascript
JavaScript实现简单的计算器
2020/01/16 Javascript
微信跳一跳辅助python代码实现
2018/01/05 Python
浅谈Python3中strip()、lstrip()、rstrip()用法详解
2019/04/29 Python
PyQt5笔记之弹出窗口大全
2019/06/20 Python
python 实现生成均匀分布的点
2019/12/05 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
2020/02/07 Python
Python-openpyxl表格读取写入的案例详解
2020/11/02 Python
德国最大的拼图在线商店:Puzzle.de
2016/12/17 全球购物
泰国综合购物网站:Lazada泰国
2018/04/09 全球购物
Lookfantastic美国/加拿大:英国知名美妆购物网站
2019/03/27 全球购物
解决方案设计综合面试题
2015/08/31 面试题
交通安全演讲稿
2014/01/07 职场文书
大课间活动制度
2014/01/18 职场文书
迎八一活动主题
2014/01/31 职场文书
抄作业检讨书
2014/02/17 职场文书
原材料检验岗位职责
2014/03/15 职场文书
综治宣传月活动总结
2014/04/28 职场文书
电气工程及其自动化专业毕业生自荐信
2014/06/21 职场文书
2014年企业党建工作总结
2014/12/18 职场文书
如何解决springcloud feign 首次调用100%失败的问题
2021/06/23 Java/Android
微软Win11什么功能最惊艳? Windows11新功能特性汇总
2021/11/21 数码科技