基于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(三)--- Node.js模块
May 25 NodeJs
NodeJS学习笔记之网络编程
Aug 03 NodeJs
nodejs实现的一个简单聊天室功能分享
Dec 06 NodeJs
nodejs简单实现操作arduino
Sep 25 NodeJs
Nodejs 搭建简单的Web服务器详解及实例
Nov 30 NodeJs
详解nodeJS中读写文件方法的区别
Mar 06 NodeJs
nodejs判断文件、文件夹是否存在及删除的方法
Nov 10 NodeJs
nodejs acl的用户权限管理详解
Mar 14 NodeJs
NodeJS父进程与子进程资源共享原理与实现方法
Mar 16 NodeJs
NodeJS使用Range请求实现下载功能的方法示例
Oct 12 NodeJs
NVM安装nodejs的方法实用步骤
Jan 16 NodeJs
NodeJS和浏览器中this关键字的不同之处
Mar 03 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使用strtotime和date函数判断日期是否有效代码分享
2013/12/25 PHP
PHP解码unicode编码的中文字符代码分享
2014/08/13 PHP
PHP中is_file()函数使用指南
2015/05/08 PHP
laravel excel 上传文件保存到本地服务器功能
2019/11/14 PHP
checkbox 多选框 联动实现代码
2008/10/22 Javascript
jquery1.4.2 for Visual studio 2010 模板文件
2010/07/14 Javascript
js继承的实现代码
2010/08/05 Javascript
修复IE9&safari 的sort方法
2011/10/21 Javascript
JQuery中如何传递参数如click(),change()等具体实现
2013/04/28 Javascript
分享经典的JavaScript开发技巧
2015/11/21 Javascript
javascript实现2016新年版日历
2016/01/25 Javascript
AngularJS使用ng-Cloak阻止初始化闪烁问题的方法
2016/11/03 Javascript
浅谈 Vue v-model指令的实现原理
2017/06/08 Javascript
微信小程序页面调用自定义组件内的事件详解
2019/09/12 Javascript
Jquery 动态添加元素并添加点击事件实现过程解析
2019/10/12 jQuery
微信小程序 flexbox layout快速实现基本布局的解决方案
2020/03/24 Javascript
超详细小程序定位地图模块全系列开发教学
2020/11/24 Javascript
element-ui中el-upload多文件一次性上传的实现
2020/12/02 Javascript
利用python实现数据分析
2017/01/11 Python
利用python实现命令行有道词典的方法示例
2017/01/31 Python
Python正则抓取网易新闻的方法示例
2017/04/21 Python
Python使用爬虫抓取美女图片并保存到本地的方法【测试可用】
2018/08/30 Python
python random从集合中随机选择元素的方法
2019/01/23 Python
Python实现合并两个有序链表的方法示例
2019/01/31 Python
python 利用文件锁单例执行脚本的方法
2019/02/19 Python
Python拼接字符串的7种方式详解
2020/03/19 Python
Python要求O(n)复杂度求无序列表中第K的大元素实例
2020/04/02 Python
MxNet预训练模型到Pytorch模型的转换方式
2020/05/25 Python
ASOS亚洲:ASOS Asia
2018/03/04 全球购物
《都江堰》教学反思
2014/02/07 职场文书
项目建议书模板
2014/05/12 职场文书
车间核算员岗位职责
2014/07/01 职场文书
稽核岗位职责范本
2015/04/13 职场文书
用人单位聘用意向书
2015/05/11 职场文书
婚宴致辞
2015/07/28 职场文书
PyTorch 如何自动计算梯度
2021/05/23 Python