ASP.NET没有魔法——ASP.NET MVC Razor与View渲染 ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

时间:2023-03-09 01:15:34
ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

ASP.NET没有魔法——ASP.NET MVC Razor与View渲染

  对于Web应用来说,它的界面是由浏览器根据HTML代码及其引用的相关资源进行渲染后展示给用户的结果,换句话说Web应用的界面呈现工作是由浏览器完成的,Web应用的原理是通过Http协议从服务器上获取到对应的Html代码以及相关资源,使得浏览器能够完成正确的呈现工作。

  ASP.NET MVC作为一个Web应用构建框架View承担了UI显示的功能,在开发过程中View以Action的名称命名,当用户的请求被路由到某一Action方法时,ASP.NET MVC将会根据Action的名称来获取到对应的View文件,将该View文件动态处理后生成最终的Html内容,将内容返回到浏览器进行显示。所以ASP.NET的渲染实际上指的是动态的生成Html代码的过程。
  而ASP.NET MVC中action的代码可以简单如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理 

  仅需要调用一个View方法就可以将Index这个View显示到用户的浏览器上,那么View方法到底做了什么处理?Razor是什么?Action方法的返回值ActionResult又是什么?
  本文将从以下几个方面来介绍ASP.NET MVC Html代码的生成过程:
  ● ActionResult及ViewResult
  ● View的查找与Razor
    ○ ViewEngineCollection&ViewEngine
    ○ ViewEngineResult
  ● View的编译与激活
  ● View的渲染
  ● 使用示例代码演示View的渲染过程
  ● View的Html Helper与ModelMetadata
  ● 常用的ActionResult
  ● 小结

ActionResult及ViewResult

  在之前的文章《ASP.NET没有魔法——ASP.NET MVC 过滤器(Filter)》中提到过,Action方法是由ActionInvoker完成执行的,Action返回的结果是一个ActionResult类型,Action执行后ActionInvoker又调用了ActionResult的ExecuteResult方法完成特定的操作,相关代码如下所示:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ActionResult的定义如下,它包含了一个名为ExecuteResult的方法,该方法用来完成对action方法执行结果进行处理:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  回到最初提到的View()方法,该方法定义在Controller中,它的返回值是一个ViewResult类型:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  可以这么说,当Action执行完成后,ASP.NET MVC的View渲染工作是由ViewResult在ExecuteResult方法中完成的,ViewResult的ExecuteResult实现代码如下(注:该代码在ViewResult的基类ViewResultBase中实现):

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从代码中可以容易的看出,ASP.NET MVC中View的渲染工作主要有四步:
  1. 如果没有指定View的名称,那么默认以Action的名称为View名称。
  2. 根据控制器的上下文查找并获得真实的View对象。
  3. 调用View对象的Render方法将View的内容写到HttpContext的响应信息中,后续将其返回至浏览器。
  4. 释放View对象。

  根据上面的分析View渲染的两个重要步骤就是View对象的查找渲染,其整个过程可参考下图,详细内容将在后续介绍:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

View的查找与Razor

  在ASP.NET MVC中View文件一般放置在项目根目录的Views目录下,以Controller名称为子目录,每一个子目录下保存了以action方法名称命名的View文件:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ViewResult类型中查找View的代码如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从代码可以看出它是通过一个ViewEngineCollection对象,根据ViewName(默认是actionName)去查找View的,如果找到返回一个ViewEngineResult类型,否则将抛出异常,异常中包含查找的位置:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:从上面的错误信息中可以看到ASP.NET MVC在查找View时,除了匹配了.cshtml和.vbhtml的文件外,还匹配了.aspx和.ascx的文件,后者是Web Form框架的页面文件,这是为什么呢?因为默认情况下ASP.NET MVC中会包含MVC使用的Razor 引擎和Web Form使用的Web Form引擎,所以在纯使用MVC开发的情况下,为了优化性能,一般会通过以下代码将Web Form的引擎删除:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  更多View引擎的内容后续介绍。

ViewEngineCollection&ViewEngine

  在ASP.NET中有一个IViewEngine的接口,它定义了查找和释放View,其定义如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  而ASP.NET中实现IViewEngine接口的类型关系如下图:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从该图中可以得到一下信息:
  ● ASP.NET中有两个最终实现的ViewEngine,分别是Razor和WebForm,MVC应用中使用Razor实现View的渲染。
  ● 它们的基类都是VirtualPathProviderViewEngine,就是说它们都是基于相对路径来管理View的。
  ● 它们的基类都是BuildManagerViewEngine,表面它们都和编译有关(注:在ASP.NET中无论是WebForm还是MVC,都可以在页面上编写代码,而这些代码肯定是不能被浏览器理解的,需要经过编译才能够正常工作)。

  ASP.NET中的ViewEngine被一个名为ViewEngines的集合进行管理,如下图:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  MVC中主要使用的是RazorViewEngine,下图是RazorViewEngine的部分代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从代码中可以看到两个重要信息,第一是“_ViewStart”被硬编码为启动页面,这也是为什么在该页面指定布局的原因,另外在其构造方法中硬编码了各种LocationFormats,它们指定了相应类型页面的搜索路径

  那么上面提到的Razor又是什么呢?Razor是ASP.NET的一种可以将服务器代码嵌入到网页中的标记语言,它由Razor标记、服务器代码(C#/VB)以及Html代码组成。在Html中以@符号开始的内容将会被识别为服务器代码,而Razor将识别这些代码将其渲染为Html代码。更多关于Razor的内容可参考:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor

ViewEngineResult

  ViewEngineResult是ViewEngine对View查找后结果的封装,其定义如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  它包含了查找后的结果IView类型以及用于查找的ViewEngine本身,另外还有一个字符串列表用来保存查找该View所遍历过的路径。
  下面是RazorViewEngine用来查找和创建RazorView对象的主要代码,其核心实际就是根据action名称和Controller名称与相应的LocationFormats匹配后查找文件是否存在,如果存在则创建IView实例的过程:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  而这里的IView类型就是RazorView:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

View的编译与激活

  上面介绍了ViewEngine用来查找并获取相应IView对象,那IView是用来做什么的呢?下图是IView的接口定义,它只有一个Render方法,用来将指定的View上下文信息通过指定的TextWriter进行渲染,这实际上就是将View文件的内容处理后,写到Http响应数据的过程:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET中实现IView的类结构如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  同样的有两种View分别对象Razor和WebForm,而它们的基类都是BuildManagerCompiledView(被BuildManager编译后的View),而且Render方法也是在基类中实现的,具体代码如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  RenderView方法实现在对应的子类中,下图为RazorView的RenderView方法:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  上面代码的核心在于:
  1. 通过BuildManager根据View文件的路径对View文件进行编译,并获得编译后的类型(注:BuildManager是ASP.NET中用于对程序集、页面进行编译的工具)。
  2. 通过激活器创建View编译后的类型,下图是默认使用的激活器,其核心是通过依赖解析器或者Activator来直接创建类型实例。

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  3. 实例化后的对象是一个WebViewPage类型,通过对WebViewPage初始化后(包含起始页的查找)调用WebViewPage的ExecutePageHierarchy方法完成渲染。

  注:WebViewPage是Razor对应的页面类型,WebForm对应的是ViewPage和ViewUserControl。

View的渲染

  上面提到View文件编译后是一个WebViewPage对象,而View的渲染也是由该对象完成的,那么WebViewPage是什么?下图是WebViewPage的定义:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从中可以看到一些重要的属性如Html、Ajax、Url等这些可以在View里面使用的,有用来生成Html、Url、Ajax的帮助类型,也有如携带了数据用于绑定到View上的Model、TempData、ViewBag、ViewData等类型。
  另外WebViewPage继承至WebPageBase:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  WebPageBase类型里面定义了RenderBody、RenderSection等方法。
  了解了WebPage与WebPageBase之后有没有感觉View文件实际上是WebPage的一个子类型,在View中可以随意使用和调用WebPage和WebPageBase中的属性和方法。
  下图是对Contact.cshtml文件编译后的代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从代码中证明了之前的猜想,View文件编译后确实是WebViewPage的子类型,而该类型中的Execute方法是将Html代码以字符串的形式进行了拼接,拼接过程中如果遇到特殊方法的调用则拼接特殊方法的返回值:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  以上代码来自布局文件的编译结果。
  而Execute方法也就是最终ASP.NET MVC进行View渲染的实际方法。WebViewPage的ExecutePageHierarchy是因为MVC中页面可能依赖多个View,如默认情况下页面有StartPage中指定的布局View和内容View,为了保证渲染内容顺序不变ExecutePageHierarchy方法中引入了栈机制(后进先出)。

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:BuildManager编译的View结果默认路径为"%WinDir\Microsoft.NET\Framework\ {Version No}\Temporary ASPNET Files"目录下,以App_Web_开头的程序集中,程序集的名称是根据路径随机生成的。

使用示例代码演示View的渲染过程

  下面就用代码的方式来演示View的查找、编译、激活以及渲染的全过程:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  全量代码:

ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理
 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Linq;
 5 using System.Web;
 6 using System.Web.Compilation;
 7 using System.Web.Mvc;
 8 using System.Web.WebPages;
 9
10 namespace My_Blog.Controllers
11 {
12     public class ViewRenderController : Controller
13     {
14         // GET: ViewRender
15         public void Index()
16         {
17             var path = "";
18             var viewEngineResult = this.FindView(out path);//查找View
19             Render(viewEngineResult, path);//渲染View
20         }
21
22         //View的查找,相当于RazorViewEngine
23         private ViewEngineResult FindView(out string path)
24         {
25             var actionName = "Contact";
26             var controllerName = "Home";
27             var viewLocationFormat = @"~/Views/{1}/{0}.cshtml";
28             //根据Controller和Action名称与地址模板组成View相对路径
29             path = string.Format(viewLocationFormat, actionName, controllerName);
30             //根据文件路径创建RazorView和ViewEngineResult
31             var view = new RazorView(this.ControllerContext, path, "", true, null, null);
32             return new ViewEngineResult(view, new RazorViewEngine());
33         }
34
35         //View的渲染
36         private void Render(ViewEngineResult viewEngineResult,string path)
37         {
38             Type pageType = BuildManager.GetCompiledType(path);//根据对View文件进行编译
39             var pageInstance = Activator.CreateInstance(pageType);//创建View文件编译后类型实例
40             var webViewPage = this.InitViewPage(pageInstance, viewEngineResult, path);//对实例中相关属性进行初始化
41             webViewPage.ExecutePageHierarchy(//完成View的渲染
42                 new WebPageContext(this.HttpContext, null, null),
43                 this.HttpContext.Response.Output, null);//startpage设置为null,将不会渲染布局页面
44         }
45
46         private WebViewPage InitViewPage(object instance, ViewEngineResult viewEngineResult, string path)
47         {
48             WebViewPage webViewPage = instance as WebViewPage;
49
50             if (webViewPage == null)
51             {
52                 throw new InvalidOperationException("无效");
53             }
54             ViewContext viewContext = new ViewContext(this.ControllerContext,
55                 viewEngineResult.View,
56                 this.ViewData,
57                 this.TempData,
58                 this.HttpContext.Response.Output);
59             webViewPage.VirtualPath = path;
60
61             webViewPage.ViewContext = viewContext;
62             webViewPage.ViewData = viewContext.ViewData;
63             webViewPage.InitHelpers();
64             return webViewPage;
65         }
66     }
67 }
ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  上面的代码要点如下:
  ● FindView方法实际上代表的就是RazorViewEngine,它根据硬编码的View文件搜索模板结合Controller和Action的名称获得View文件的全路径,并创建RazorView和ViewEngineResult对象。
  ● Render则代表了View的编译、激活以及渲染过程。

  运行结果(由于没有布局页面,所以相关的样式和JS都没有被引入):

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

View的Html Helper与ModelMetadata

  在ASP.NET MVC的View中可以通过一些Helper类型来生成HTML代码,下图为用户注册页面的View代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从代码中可以看到ASP.NET MVC并不是完全使用Html来构成页面的,中间有一些通过Html属性(注:Html是WebViewPage类型中的HtmlHelper类型的实例)调用的方法,从方法的名称来看这些方法分别用于生成如数据验证信息、Label标签、文本框以及密码类型文本框等HTML代码。
  ASP.NET MVC提供了一系列的Helper类及其拓展方法,这些Helper类中封装了针对HTML、Ajax(ASP.NET MVC中Ajax Helper的用法可参考:https://www.c-sharpcorner.com/article/Asp-Net-mvc-ajax-helper/)等相关内容的实现,ASP.NET MVC对Html拓展可以分为以下四类:
  1. 用于生成特定标签的拓展,如Form、Input、Label、TextArea、Select等拓展。
  使用方式如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  将常用的HTML标签进行封装,对于不熟悉Html的开发人员来说ASP.NET MVC提供了一种面向对象编程的方式来对页面进行开发,更重要的是Html方法可以与模型关联,当模型的元数据中有相应的验证特性并且开启了客户端验证时,在渲染标签时会包含相应的验证信息,使用这类标签最大的好处就是可以将关注点全部放在模型上,当模型发生变化时View上不需要做任何的修改。

  2. 用于生成Model验证信息的拓展。
  使用方式如下:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET MVC中提供了模型验证的机制,当模型验证失败时会有相应的失败信息,而该拓展就是对这些错误信息进行渲染,大大的减少了输出验证信息的工作量。

  3. 根据用途“展示/编辑”生成标签的Display以及Editor拓展。
  使用方式如下(注:DisplayFor是用于显示指定模型属性中的值,如果要显示对应模型属性的名称可用DisplayNameFor):

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  Display与Editor是根据模型的数据类型来判断如何对模型进行展示。ASP.NET MVC中为Display和Editor提供了一些基础类型的默认模板实现分别通过内部静态类型DefaultEditorTemplates和DefaultDisplayTemplates进行存储,下面是bool类型模板代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  在提供默认模板的同时ASP.NET MVC也提供了自定义模板的机制,可以分别使用DisplayAttribute与UIHintAttribute对特定属性指定渲染模板,如何自定义Display和Editor模板可参考:http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html

  4. Partial拓展。
  使用方法(注:第一个参数是Partial View的名称,默认情况下Partial View文件存储于Views/Shared目录下,如果文件不在这个目录下需要在参数中体现具体目录):

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  Partial是ASP.NET MVC中用于将可重用Html进行分离的机制,并且Partial是可以访问数据的,就是说通过parital分离的Html代码,可以根据传入的数据来动态生成Html代码,更多关于Partial View的内容可参考.Net Core的文档:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial

  5. Child Action拓展。
  使用方法:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  Child Action类似于Partial View,它也是将可重用的部分进行分离,但Partial View更侧重于关注Html代码重用,Child Action还包含了后端逻辑的重用。如购物网站的购物车,它可能出现在任意的页面中,但首页的逻辑、模型与购物车就可能没有任何关系,此时就可以使用Child Action。

常用的ActionResult

  前面提到过ASP.NET MVC的页面渲染工作实际上是由一个继承至ActionResult的ViewResult对象完成的,ActionResult实际上是ASP.NET MVC中的一个抽象,代表了所有逻辑执行后的结果,而ViewResult是将结果面向人的,所以返回了Html让浏览器显示给人看,除了ViewResult之外还有一些常用的ActionResult如下:
  ● ContentResult:用于将字符串返回到客户端,在Action方法中调用Content方法返回。
  ● FileStreamResult:用于将文件返回到客户端,在Action方法中调用File方法(有多个重载)返回。
  ● HttpNotFoundResult:用于返回HTTP未找到状态,在Action方法中调用HttpNotFound方法返回。
  ● JavaScriptResult:用于将JavaScript返回到客户端并执行,在Action方法中调用JavaScript方法返回。
  ● JsonResult:用于将Json数据返回到客户端,在Action方法中调用Json方法返回。
  ● PartialViewResult:用于渲染partial页面,在Action方法中调用PartialView方法返回。
  ● RedirectResult:用于重定向,在Action方法中调用Redirect方法传入需要重定向的Url进行重定向操作。
  ● RedirectToRouteResult:用于路由重定向,在Action方法中调用RedirectToAction方法重定向到指定的Action。
  ● EmptyResult:返回空,在Action方法中返回Null或将Action方法的返回值设为Void即可。

小结

  本文介绍了ASP.NET MVC如何在Action方法执行时通过对ActionResult(ViewResult)的执行完成View文件的查找、编译以及渲染的过程,除了ViewResult之外ASP.NET MVC还提供了其它类型的ActionResult如Json、File等,使用这些结果可以创建简单的Web API以及文件下载等功能,另外MVC通过Html Helper类型对View进行了拓展,在开发View时可以最大程度的对View的Html代码和逻辑进行重用,同时也将View与Model(特指ViewModel)进行关联,在开发时可以将关注点放在Model上,无需担心Model修改后View代码的修改。合理的使用View提供的相关机制,可以极大的减少工作量同时也可以让代码变得更加简洁。

参考:
  https://zhuanlan.zhihu.com/p/29418126
  https://www.codeproject.com/Articles/787320/An-Absolute-Beginners-Tutorial-on-HTML-Helpers-and
  https://www.c-sharpcorner.com/article/Asp-Net-mvc-ajax-helper/
  http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html
  https://*.com/questions/5037580/asp-net-mvc-3-partial-vs-display-template-vs-editor-template
  https://weblogs.asp.net/scottgu/introducing-razor
  https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor

本文链接:http://www.cnblogs.com/selimsong/p/8670744.html

ASP.NET没有魔法——目录

ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  对于一个应用来说界面的重要性无言而喻,而Web应用的界面是使用Html+Css以及Javascript实现的,ASP.NET MVC是一个用来构建Web应用的框架,它的界面也是Html实现的,对于一些开发团队来说,一般Web项目会存在专业的UI前端工程师和后端工程师,前端工程师可能只懂设计和Html,但是对于如何将设计好的Html应用到ASP.NET可能就需要ASP.NET工程师的帮助。
  本文将介绍如何将已经设计好的Web界面应用到ASP.NET MVC中以及如何对这些资源文件进行管理,先看一下修改后的效果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理  

  本章的主要内容有:
  ● 素材选择
  ● ASP.NET MVC的界面布局及界面实现
  ● 使用BundleConfig进行素材资源管理
    ○ 使用bundle对素材分类
    ○ 使用Bundle对资源文件进行优化
    ○ 在Bundle中使用通配符及文件版本(min版)的选择
    ○ 使用CDN
    ○ Bundle中的缓存
    ○ Bundle重写样式表中资源引用路径
    ○ 自定义资源转变(Transform)
  ●小结

素材选择

  本文选取开源主题Start Bootstrap - Clean Blog为例进行介绍。
  Clean Blog是一个现代风格的响应式主题,基于bootstrap 4.0,下图为Clean Blog的运行效果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  将Clean Blog下载到本地并导入《My Blog》项目中,该主题中包含了相应的样式表、图片、Js、示例页面等文件:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:Clean Bolg的GitHub地址:https://github.com/BlackrockDigital/startbootstrap-clean-blog

ASP.NET MVC的界面布局及界面实现

  Clean Blog是由Html、Css、Javascript文件组成的一个静态Web界面,要将其应用到ASP.NET MVC中,只需要对其结构进行分析后一一替换到ASP.NET MVC的View中即可。
所以首先要分析的是Clean Blog以及My Blog应用的页面布局,对于Clean Blog来说它分为三块,分别是导航、内容以及页脚:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  同样的My Blog之前使用的ASP.NET MVC默认模板也是分为了导航、内容和页脚:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  对于上面的布局来说,导航和页脚部分是不变的,只有中间的内容是变化的,在ASP.NET MVC中提供了布局页的机制,专门用来定义页面布局,将不变的内容放置在该布局页面上,所以要更换界面首先需要的就是定义布局页面:
  在Views/Shared目录下添加一个新的布局页面,将Clean Blog的Index页面中的导航以及页脚代码复制到新的布局页面中,包括css以及js的引用(注:需要修改路径),页面内容部分使用@RenderBody()方法代替:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  同时将_ViewStart.cshtml中指定的布局文件改为新创建的CleanBlog布局文件:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  最后参照Clean Blog内容页样式完成相应内容页面修改即可,下面以“联系我们”页面为例:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  运行效果(注:没有实现原主题中的信息提交功能):

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  

使用BundleConfig进行素材资源管理

  通过上面的介绍知道了如何通过布局页面来搭建页面内容,Web页面除了本身的Html代码外还少不了css、JavaScript等文件的支持,但这些文件都是相对零散的,在ASP.NET中有一个Bundle机制专门用于管理这些资源文件。
  Bundle有捆和包的意思,而在ASP.NET MVC中它实际就是可以将一组css或JavaScript捆绑起来,捆绑可以根据资源的一些特性进行归类,与此同时还添加了一些有用的附加功能,如:文件最小化、资源文件的版本管理、使用CDN加速、资源文件的缓存等等,下面就介绍如何使用bundle来管理新添加到项目中的素材资源。

使用bundle对素材分类

  为了保证页面样式一致,所以在创建布局文件时添加了全站共用的样式和脚本,Clean Blog是基于Bootstrap4.0建立的,除此之外还有一些特殊的内容,如下图所示,它的实例代码中已经进行过分类:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  样式主要包括Bootstrap的核心样式、模板中使用的字体以及模板中的自定义样式,同样JavaScript除了bootstrap必要的JavaScript外还有自定义的JavaScript。根据这个分类规则,在App_Start/BundleConfig的BundleConfig类型中注册分类:

  样式分类:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  脚本分类:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:建议bundle的地址以bundles作为前缀,避免与路由冲突。
  完成后在布局文件中改用Bundle渲染样式和脚本:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

使用Bundle对资源文件进行优化

  前面通过Bundle对资源进行了分类,当同一类资源下存在多个文件时,页面只需要一句代码就可以全部引入使代码更清晰,当然Bundle机制在资源分类的同时,更重要的是可以对资源文件进行优。
  这里的优化有两个点分别是文件请求的优化和文件大小的优化,前者是将同一类的文件进行合并,合并成一个文件,在请求时只需请求一次即可,后者是将css及JavaScript中不需要的字符删除、变量名称简写以达到缩减文件尺寸的目的。
  使用Bundle管理资源时,在Release模式下将自动进行优化,另外也可以设置BundleTable.EnableOptimizations为true进行强制优化,如下图所示:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  运行时将可以看到如下结果:
  1. 请求数量减少:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  2.非最小化的js文件被最小化,如clean-blog.js:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:上面请求中出现字体文件无法找到的错误将在后续内容解释。

在Bundle中使用通配符及文件版本(min版)的选择

  对于一些样式或脚本组件来说,它本身就可能由多个文件构成,如下图中的Jquery目录中就有jquery.js以及jquery.slim.js两个文件:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  在一些复杂的应用中还可能会使用到jQuery的其它组件,这样资源文件会更多,为了简化Bundle对资源的归类,在使用Bundle注册分类时,可以使用通配符来一次匹配多个文件:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理  

  注:Bundle中能识别的通配符有两种,常用的就是“*”,另外还可以通过“{version}”来匹配文件版本。更多通配符相关内容可参考:https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification#using-the--wildcard-character-to-select-files
  直接使用*通配符可以匹配相应目录下的所有文件,下图是运行结果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  从图中可以看到,比之前的请求中多了一些js文件,这些文件就是通过通配符匹配到的,但要注意的是这里没有获取包含.min版本的js文件,在相同目录下clean-blog.js以及jquery.js都有被最小化的min版本:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  Bundle机制会根据debug/release模式或者BundleTable.EnableOptimizations属性来判断是否对资源文件优化,在release模式(web.config中compilation的debug属性为true)下或者BundleTable.EnableOptimizations设为true时会启用资源优化,这里的资源优化除了上面提到的多个文件捆绑外,如果文件列表中有min版本文件,那么就会优先选择min版本文件。
  由于优化模式下无法看到具体请求的文件名称,所以在clean-blog.min.js中添加一条日志输出代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  然后使用release模式运行程序,将会获得下面结果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  除了看到请求减少以外,还看到日志中输出了min版本文件中添加的内容,证明使用release模式运行时Bundle会自动选择文件最小化的版本。

使用CDN

  CDN(内容分发网络),为了提高web的响应速度,其中一项主要的优化手段就是将静态资源放到CDN上,这样用户就可以在离他最近的网络节点获取到这些资源,这样既提高了资源获取的速度同时也降低了应用服务器的压力,ASP.NET中的Bundle可以为相应的资源配置CDN,并且该配置也是release模式下生效:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  使用CDN主要有以下几个步骤:
  ● 将bundles的UseCdn属性设为true。
  ● 创建Bundle对象时构造方法中传入CDN的路径。
  ● 设置Bundle对象的CdnFallbackExpression属性,该属性用于判断通过CDN加载的内容是否正确加载,如果没有那么会加载Include中的内容。

  运行效果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  将bundles.CdnFallbackExpression的属性设置为window.jQuery会生成一个判断window.jQuery对象是否存在,如果不存在则获取服务器资源的代码:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  通过修改CDN资源路径,模拟CDN无法访问情况,它会自动加载可用的资源:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  CDN可用资源参考:http://www.bootcdn.cn/

Bundle中的缓存

  在Release或资源优化模式下,Bundle为其管理的资源提供了缓存机制,在第一次访问Bundle管理的资源时,Bundle会为每一组资源生成一个唯一标识,然后将该资源默认缓存一年:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  当资源发生改变时唯一标识会发生变化,那么原来缓存的内容就自动失效了。

Bundle重写样式表中资源引用路径

  在前面的介绍的过程中release模式下一直有一个错误,就是无法找到字体文件,这是因为在编写样式时会引入一些字体或者图片等外部资源,而这些资源一般是用相对路径进行引用的,但是当ASP.NET中使用Bundle来对样式资源进行捆绑时,该资源的url地址就发生改变了,导致使用该地址组合的引用资源的地址不正确,从而导致了资源无法加载:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  为了解决这一问题,Bundle提供了一个重写样式引用相对路径的解决方案,在将样式表注册到Bundle中时,可以为相应的样式文件添加一个CssRewriteUrlTransform对象,它用于Css文件优化时对其引用的Url进行重写:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  添加以上代码后,运行程序将解决字体无法找到的问题:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

自定义资源转变(Transform)

  资源转变是Bundle机制在优化资源时提供的一个可拓展接口,如上面介绍的css Url重写就是资源转变的拓展,Bundle中实现资源转变需要实现以下接口:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  接口中的参数分别代表获取到文件的虚拟路径和文件的内容。
  下面创建一个在css中插入自定义内容的资源转换器,来介绍如何实现资源转变的自定义拓展:
  实现IItemTransform接口,在样式文件中追加".test {color: red;}"样式:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  将该转换器通过Include方法应用到对应文件上:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  运行结果显示相应内容已经被添加到最终的样式文件中:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  注:IItemTransform接口是在资源优化前调用的,所以本例添加内容包含的空格和分号最终都被优化删除了,所以在使用IItemTransform进行拓展时需要注意资源优化问题。
  另外在Bundle中还有另外一个拓展接口IBundleTransform,它定义了一个用于转换Bundle相应文件的方法:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  实现Process接口,在响应的内容上添加注释“hello selim”:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  BundleTransform需要在Bundle的Transforms属性上添加:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  运行结果:

  ASP.NET没有魔法——ASP.NET MVC Razor与View渲染         ASP.NET没有魔法——ASP.NET MVC界面美化及使用Bundle完成静态资源管理

  相对与ItemTransform来说BundleTransform处理后的内容不会再进行优化,在文档https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification#less-coffeescript-scss-sass-bundling中还通过BundleTransform实现了less等文件动态编译的功能,有兴趣可参考该文档。

小结

  本章主要介绍了如何将现有的Web样式应用到ASP.NET MVC中,并着重介绍了ASP.NET MVC中对静态资源管理的Bundle机制。Bundle机制除了可以对资源文件进行归类外还提供了资源文件优化、CDN、缓存等高级功能。合理的利用Bundle可以让项目代码更清晰,同时也可以提高应用的性能。

参考:
  https://github.com/BlackrockDigital/startbootstrap-clean-blog
  https://html.com/attributes/
  https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification
  https://www.codeproject.com/articles/842961/introducing-dynamic-bundles-for-asp-net-mvc
  https://*.com/questions/11355935/mvc4-stylebundle-not-resolving-images#
  http://www.bootcdn.cn/
  http://www.tutorialsteacher.com/mvc/scriptbundle-mvc
  https://www.codeproject.com/Articles/728146/ASP-NET-MVC-bundles-internals
  https://blogs.msdn.microsoft.com/rickandy/2011/05/21/using-cdns-and-expires-to-improve-web-site-performance/

本文链接:http://www.cnblogs.com/selimsong/p/8589175.html

ASP.NET没有魔法——目录