现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了系统的可维护性和可扩展性。于此同时,HTML5提供了很多的新特性,比如新的架构元素、代替cookie的Web存储技术、WebSocket等,也使得网站应用能够更好适应新的商业环境和技术更新。

本系统的网站模块使用.NET技术堆栈中的ASP.NET MVC框架,此框架是微软公司推出的开源框架,相关源代码可以在Codeplex.com网站上找到。该技术框架经过7年多的发展,当前已发展到5.1版本,新增了包括移动客户端模板、WebAPI模板、OAuth认证等功能在内的新特性,便于当前的以互联网为主题背景的项目开发。

在实际的项目中,主要面临的两个问题分别是:实际用户使用的终端设备的厂商、型号等可能千变万化,如何在不同的Web终端上呈现出适合的样式;ASP.NET MVC默认提供WebForm和Razor两种视图引擎,但他们并不能支持HTML5页面的呈现,如何扩展视图引擎用于支持HTML5页面的渲染。实际选择的技术解决方案是,使用Media Queries等技术手段来实现响应式的CSS3设计,用自定义静态HTML5视图引擎扩展ASP.NET MVC框架。接下来,将分别详细介绍两个解决方案在项目中的实施应用。

首先,介绍响应式的网页布局,这个概念首先由Ethan Marcotte于2010年5月提出,目的是使得一个网站可以兼容多个终端—而不是为每一个终端开发一个特定的版本,可以说它就是为解决移动互联网相关痛点应运而生的。实际上,当前正在运营的很多大型网站上,仍然是维护多套的网站程序,有的还使用不同的域名来服务不同的终端。同时往往对移动端网页中的业务功能进行了一定的删减,虽然也能提供不错的用户体验,但这并不是最合理的解决方案,尽可能的统一用户接口是很有必要的。响应式的页面布局在具体的实现上,主要包含以下的几个步骤。

第一步,在html页面的head部分增加名为viewport的meta元素,viewport表示一个虚拟的窗口,通过它来设置适应移动设备屏幕的大小,代码如下所示。

  <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=0" />
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>

Content属性中"width=device-width",表示宽度为当前设备宽度;"intial-scale=1"表示初始缩放倍数为1,;"user-scalable=0"表示不支持用户手动缩放。同时增加css3-mediaqueries.js用于兼容IE6等老式浏览器。

第二步,使用Media Queries模块来根据不同的设备的可视屏幕大小来导入不同的CSS文件。该模块的应用需要修改两部分的内容,一部分是在HTML文件中增加3个不同条件下的CSS文件,另一部分是在CSS文件使用指定的形式将原有的内容包装起来。代码如下所示。

 //Html文件中:
<link rel="stylesheet" type="text/css" media="only screen and (min-width:900px)" href="/content/theme/base/normalScreen.css" />
<link rel="stylesheet" type="text/css" media="only screen and (min-width:480px), only screen and (max-device-width:900)" href="/content/theme/base/smallScreen.css" />
<link rel="stylesheet" type="text/css" media="only screen and (max-width:480px)" href="/content/theme/base/tinyScreen.css" />
//CSS文件中:
@media screen and (min-width:900px) {
/*正常CSS内容,省略*/
}

在HTML文件中,Media属性中"screen and (min-width:900px)"表示媒体类型为屏幕,同时屏幕的最小宽度为900像素,only关键字使得不支持Media Queries的设备忽略该样式文件,之后href属性中为当前条件下所引用的CSS文件路径。通常来说屏幕可见宽度小于480像素的设备为手机,介于480像素到900像素之间的为平板电脑,大于900像素的为台式机电脑。在CSS文件中,通过添加@media段与页面中media属性进行映射,其块中所包含的内容与一般传统网站的该文件相似,针对不同的设备,通过继承的方式对样式的布局进行一些细节的调整。

最后一步,主要是处理前端开发中一些细节,包括使用相对的宽度,相对的字体大小、流动的布局、自适应的图片等内容,和传统的页面开发相似,在此就不一一展开。最终的效果图如下,可以看到同样的内容在不同的设备上得到不一样的渲染,以下是应用响应式页面布局技术的效果图。

接下来介绍如何在ASP.NET MVC框架中扩展自定义的视图引擎,使得框架能与HTML5技术无缝的衔接。之所以选择ASP.NET MVC框架作为Web前端主要构件基础,除了之前所提到的一些支持移动互联网方面的新特性外,它内生所具备的高性能,高扩展性也起到了很大的作用。相对与重量级的WebForm框架,该框架显得非常的轻量级,使得页面渲染所消耗的资源得到了大幅的减少,并保留了认证、安全和本地缓存等模块的支持。此外,该框架提供了极强的扩展性,无论是自行修改框架,还是在已有项目中增减业务模块,都非常的方便。这一点,和JAVA阵营的J2EE技术很相似,但同时又具有很高的稳定性和较高的开发效率。例如可以自定义URL路由美化URL并提升搜索引擎排名,自定义数据绑定支持不同数据格式的序列化和反序列化,自定义视图引擎应对不同的业务场景等。接下来,具体介绍如何实现自定义的HTML5视图引擎,包含以下的步骤。

第一步,创建自定义的HTML5视图。它是读取HTML5文件并呈现的基础,需要实现System.Web.Mvc.IView接口,并实现接口中的Render方法,该方法主要通过流的方式读取指定的HTML5文件并渲染到页面,该部分内容比较简单,简化的代码如下所示。

 public class HTML5View : IView
{
public string FileName { get; private set; }//文件名
public HTML5View(string fileName)
{
this.FileName = fileName;
} public void Render(ViewContext viewContext, TextWriter writer)
{
byte[] buffer = null;
using (var fs = new FileStream(this.FileName, FileMode.Open))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
}
writer.Write(Encoding.UTF8.GetString(buffer));//读取文件并渲染
}
}

  第二步,创建自定义缓存。由于HTML5FileView中都是静态的内容,很自然的需要构建相应的缓存用于提高性能。创建页面的缓存Key对象HTML5ViewCacheKey,实现GetHashCode方法,通过对Controller和View的名称进行简单的Hash组合运算等到缓存内容的Key,并实现Equals方法用于比较,简化的代码如下所示。

  public class HTML5ViewCacheKey
{
public string ControllerName { get; private set; }
public string ViewName { get; private set; }
public HTML5ViewCacheKey(string controllerName, string viewName)
{
this.ControllerName = controllerName ?? string.Empty;
this.ViewName = viewName ?? string.Empty;
} public override int GetHashCode()
{
return this.ControllerName.ToLower().GetHashCode() ^ this.ViewName.ToLower().GetHashCode();
} public override bool Equals(object obj)
{
HTML5ViewCacheKey key = obj as HTML5ViewCacheKey;
if (null == key)
return false;
return key.GetHashCode() == this.GetHashCode();
}
}

  第三步,创建视图引擎HTML5ViewEngine。该类实现了IViewEngine接口,字典类viewEngineResults结合第二步中构建的缓存类用于缓存渲染后的视图。方法FindView首先解析路由信息获得控制器的名称,之后判断请求是否支持缓存,若不支持缓存,则直接通过InternalFindView方法获得视图。反之,判断请求视图是否在本地缓存中,若存在直接返回,若不存在则调用方法获取并缓存,写缓存时注意加锁。InternalFindView方法中,借助面向约定编程的思路到指定的路径中寻找到对应的文件HTML5,最终完成URL信息与文件的映射。

 public class HTML5ViewEngine : IViewEngine
{
private Dictionary<HTML5ViewCacheKey, ViewEngineResult> viewEngineResults =
new Dictionary<HTML5ViewCacheKey, ViewEngineResult>();
private static object syncHelper = new object(); public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return this.FindView(controllerContext, partialViewName, null, useCache);
} public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
string controllerName = controllerContext.RouteData.GetRequiredString("controller");
var key = new HTML5ViewCacheKey(controllerName, viewName);
ViewEngineResult result = null;
if (!useCache)//是否支持缓存
{
result = InternalFindView(controllerContext, viewName, controllerName);
viewEngineResults[key] = result;
return result;
}
if (viewEngineResults.TryGetValue(key, out result)) //视图是否已经在缓存中
{
return result;
} lock (syncHelper)//锁定视图数据写入缓存过程
{
if (viewEngineResults.TryGetValue(key, out result))
{
return result;
}
result = InternalFindView(controllerContext, viewName, controllerName);
viewEngineResults[key] = result;
return result;
}
} private ViewEngineResult InternalFindView(ControllerContext controllerContext, string viewName, string controllerName)
{
string[] searchLocations = new string[]{
string.Format("~/staticViews/{0}/{1}.html", controllerName.ToLower(), viewName.ToLower()),
string.Format("~/staticViews/Shared/{0}.html", viewName.ToLower())
}; string filename = controllerContext.HttpContext.Request.MapPath(searchLocations[]);
if (File.Exists(filename))
{
return new ViewEngineResult(new HTML5View(filename), this);
}
filename = string.Format(@"\staticViews\shared\{0}.html", viewName.ToLower());
if (File.Exists(filename))
{
return new ViewEngineResult(new HTML5View(filename), this);
}
return new ViewEngineResult(searchLocations);
} public void ReleaseView(ControllerContext controllerContext, IView view)
{
}
}

   最后一步,将视图自定义的视图引擎添加到Global.asax文件的Application_Start方法中,完成该引擎的注册。同时注意需要将该引擎的优先级设为最高,使得系统优先使用该引擎对视图相关请求进行响应。

  public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//省略
ViewEngines.Engines.Insert(, new HTML5ViewEngine());
}

通过以上步骤,基本上将ASP.NET MVC框架,HTML5技术,移动互联网开发技术有机的整合到了一起。

系列目录如下,谢谢您的阅读。

快速入门系列--MVC--01概述

快速入门系列--MVC--02路由

快速入门系列--MVC--03控制器和IOC应用

快速入门系列--MVC--04模型

快速入门系列--MVC--05行为

快速入门系列--MVC--06视图

快速入门系列--MVC--07与HTML5移动开发的结合

Tip: 本文由于是学习需要,主要参考了以下书目。

[1]蒋金楠. ASP.NET MVC4框架揭秘[M]. 上海:电子工业出版社, 2012. 419-421

[2]唐俊开. HTML5移动Web开发指南[M]. 上海:电子工业出版社, 2012. 52-54

快速入门系列--MVC--07与HTML5移动开发的结合的更多相关文章

  1. 快速入门系列--WebAPI--03框架你值得拥有

    接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...

  2. 快速入门系列--MVC--01概述

    虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...

  3. 快速入门系列--MVC--02路由

    现在补上URL路由的学习,至于蒋老师自建的MVC小引擎和相关案例就放在论文提交后再实践咯.通过ASP.NET的路由系统,可以完成请求URL与物理文件的分离,其优点是:灵活性.可读性.SEO优化.接下来 ...

  4. 快速入门系列--WebAPI--01基础

    ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...

  5. 快速入门系列--WebAPI--04在老版本MVC4下的调整

    WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...

  6. [转]快速入门系列--WebAPI--01基础

    本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...

  7. vue 快速入门 系列 —— vue-cli 上

    其他章节请看: vue 快速入门 系列 Vue CLI 4.x 上 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...

  8. vue 快速入门 系列 —— vue-router

    其他章节请看: vue 快速入门 系列 Vue Router Vue Router 是 Vue.js 官方的路由管理器.它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌. 什么是路由 ...

  9. WPF快速入门系列(4)——深入解析WPF绑定

    一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...

随机推荐

  1. tableindex

    在写代码的时候,失焦的第一反应便是ng-blur,没想到在一个标签上其作用了,多加了几个标签没反应,于是发现了tableindex,写的代码列子如下,希望可以帮助你: <img src='{{a ...

  2. PADSPCB权威指南-第一章 PADS软件系统(部分)(原创)

    PADSPCB权威指南-第一章(部分)豆丁地址:http://www.docin.com/p-707128286.html

  3. MSP430单片机的位操作

    MSP430单片机属于RISC(Reduced Instruction Set Computer)型处理器.与普通的51单片机的复杂指令集相区别.普通51单片机执行一条指令最少需要12个时钟周期,而R ...

  4. LNMP 部署

    一.防火墙配置 CentOS 7.x默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop firewalld.servic ...

  5. Kernels

    Let \(E\) be a set and  \(\mathscr{E}\)  a \(\sigma\)-algebra of subsets of  \(E\). Assume that the ...

  6. c# gridview数据列表中NamingContainer容器的用法

    当在前台我们绑定了一个linkbutton型的按钮,并触发了onserverclick="delBook_Server"的服务端事件,前台代码如下: <asp:Templat ...

  7. [ASE]项目介绍及项目跟进——TANK BATTLE·INFINITE

    童年的记忆,大概是每周末和小伙伴们围坐在电视机前,在20来寸的电视机屏幕里守卫着这个至今都不知道是什么的白色大鸟. 当年被打爆的坦克数量估计也能绕地球个三两圈了吧. 十几年过去了,游戏从2D-3D,画 ...

  8. [后端人员耍前端系列]KnockoutJs篇:使用WebApi+Bootstrap+KnockoutJs打造单页面程序

    一.前言 在前一个专题快速介绍了KnockoutJs相关知识点,也写了一些简单例子,希望通过这些例子大家可以快速入门KnockoutJs.为了让大家可以清楚地看到KnockoutJs在实际项目中的应用 ...

  9. [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

    开机初始化Log Log编号 函数名   所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...

  10. SQL面试题

    Student(S#,Sname,Sage,Ssex) 学生表     S#:学号:Sname:学生姓名:Sage:学生年龄:Ssex:学生性别Course(C#,Cname,T#) 课程表     ...