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的代码可以简单如下:

   

  仅需要调用一个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方法完成特定的操作,相关代码如下所示:

  

  

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

  

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

  

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

  

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

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

  

View的查找与Razor

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

  

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

  

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

  

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

  

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

ViewEngineCollection&ViewEngine

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

  

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

  

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

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

  

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

  

  从代码中可以看到两个重要信息,第一是“_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查找后结果的封装,其定义如下:

  

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

  

  而这里的IView类型就是RazorView:

  

View的编译与激活

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

  

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

  

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

  

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

  

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

  

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

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

View的渲染

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

  

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

  

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

  

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

  

  

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

  

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

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

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

  

  全量代码:

 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 }

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

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

  

View的Html Helper与ModelMetadata

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

  

  从代码中可以看到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等拓展。
  使用方式如下:

  

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

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

  

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

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

  

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

  

  

  在提供默认模板的同时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目录下,如果文件不在这个目录下需要在参数中体现具体目录):

  

  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拓展。
  使用方法:

  

  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://stackoverflow.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 MVC的界面布局及界面实现
  ● 使用BundleConfig进行素材资源管理
    ○ 使用bundle对素材分类
    ○ 使用Bundle对资源文件进行优化
    ○ 在Bundle中使用通配符及文件版本(min版)的选择
    ○ 使用CDN
    ○ Bundle中的缓存
    ○ Bundle重写样式表中资源引用路径
    ○ 自定义资源转变(Transform)
  ●小结

素材选择

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

  

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

  

  注: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来说它分为三块,分别是导航、内容以及页脚:

  

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

  

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

  

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

  

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

  

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

  

  

使用BundleConfig进行素材资源管理

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

使用bundle对素材分类

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

  

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

  样式分类:

  

  脚本分类:

  

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

  

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

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

  

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

  

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

  

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

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

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

  

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

    

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

  

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

  

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

  

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

  

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

使用CDN

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

  

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

  运行效果:

  

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

  

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

  

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

Bundle中的缓存

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

  

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

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

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

  

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

  

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

  

自定义资源转变(Transform)

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

  

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

  

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

  

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

  

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

  

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

  

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

  

  运行结果:

  

  相对与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://stackoverflow.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没有魔法——目录

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

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

    对于一个应用来说界面的重要性无言而喻,而Web应用的界面是使用Html+Css以及Javascript实现的,ASP.NET MVC是一个用来构建Web应用的框架,它的界面也是Html实现的,对于一些 ...

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

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

  3. Introduction to ASP.NET Web Programming Using the Razor Syntax (C#)

    1, http://www.asp.net/web-pages/overview/getting-started/introducing-razor-syntax-c 2, Introduction ...

  4. MVC Razor视图引擎

    Razor 不是编程语言.它是服务器端标记语言. Razor 是一种允许您向网页中嵌入基于服务器的代码(Visual Basic 和 C#)的标记语法 当网页被写入浏览器时,基于服务器的代码能够创建动 ...

  5. 体验 ASP.NET Core 1.1 中预编译 MVC Razor 视图

    这是从 ASP.NET Core 1.1 官方发布博文中学到的一招,可以在 dontet publish 时将 Razor 视图编译为 .dll 文件. 需要在 project.json 中添加如下配 ...

  6. Asp.net MVC Razor模板引擎技巧分享

    Razor是Asp.net MVC中新的默认模板类型, 语法简单易用.这篇文章不涉及Razor的语法,主要介绍Razor的一些在MVC项目中的使用技巧,以及脱离MVC环境下,如何使用Razor. 阅读 ...

  7. ASP.NET MVC Razor语法

    ASP.NET MVC Razor语法 (一) 关于_ViewStart.cshtml文件     使用Razor模板引擎的话,会自动生成一个_ViewStart.cshtml文件.事实上,_View ...

  8. ASP.NET MVC Razor视图引擎攻略

    --引子 看下面一段MVC 2.0的代码. <%if (Model != null){%> <p><%=Model%></p><%}%>&l ...

  9. 从ASP.Net Core Web Api模板中移除MVC Razor依赖项

    前言 :本篇文章,我将会介绍如何在不包括MVC / Razor功能和包的情况下,添加最少的依赖项到ASP.NET Core Web API项目中. 一.MVC   VS WebApi (1)在ASP. ...

随机推荐

  1. 框架—Mybatis入门

    1:Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的一个数据持久层的框架. MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果. ...

  2. [CF] 402 E. Strictly Positive Matrix

    一个矩阵,自乘无限次后能否全为正数? 如果n比较小,可以二分一下,但是这里n很大,乘一次都无法接受 可以考虑实际含义:矩阵看成邻接矩阵,那么0就是没有边,其余就是有边. 我们知道邻接矩阵自乘k次就相当 ...

  3. Mysql中max函数取得的值不是最大

    ①问题:遇到一个很有意思的问题,这里记录一下, 就是在使用max函数的时候发现取得的最大值其实不是最大值. 比如: 某一列中有10000000,和9999999, 其最大值应该是10000000但是查 ...

  4. Linux中的FTP服务

    FTP服务 文件传输协议 FTPFile Transfer Protocol 早期的三个应用级协议之一 基于C/S结构 双通道协议:数据和命令连接 数据传输格式:二进制(默认)和文本 两种模式:服务器 ...

  5. vue 框架原理

    参考:https://segmentfault.com/a/1190000006599500#articleHeader4 记录自己的理解: 核心的响应原理通过Object.definePropert ...

  6. 关于markdown 的简单使用(已更新)

    markdown的介绍 Markdown是一种可以使用普通文本编辑器编写的标记语言,通过类似HTML的标记语法,它可以使普通文本内容具有一定的格式. Markdown具有一系列衍生版本,用于扩展Mar ...

  7. Ubuntu16.04 搭建svn

    1.安装SVN 安装前先更新一下 sudo apt-get update 接下来安装 sudo apt-get install subversion 2.创建SVN版本库 1.创建home下的svn文 ...

  8. Apollo配置中心的使用

    1. 自己搭建Apollo配置中心 碰到如下错误: nested exception is org.hibernate.HibernateException: Access to DialectRes ...

  9. js中的constructor 和prototype

    参考 http://www.cnblogs.com/yupeng/archive/2012/04/06/2435386.html function a(c){ this.b = c; this.d = ...

  10. 大数据学习——装私服nexus

    (一)安装 解压之后 进入bin文件夹下 复制路径 D:\software\nexus-2.12.0-01-bundle\nexus-2.12.0-01\bin 进入命令行窗口输入以下命令,安装成功 ...