对于一个应用来说界面的重要性无言而喻,而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/