不懂代码用cms做网站,华蓥网站建设,网站cms系统下载,学校网站建设栏目设置在如何在 ASP.NET MVC 中集成 AngularJS(1)中#xff0c;我们介绍了 ASP.NET MVC 捆绑和压缩、应用程序版本自动刷新和工程构建等内容。 下面介绍如何在 ASP.NET MVC 中集成 AngularJS 的第二部分。 ASP.NET 捆绑和压缩 CSS 和 JavaScript 的捆绑与压缩功能是 ASP.NET MVC 最流… 在如何在 ASP.NET MVC 中集成 AngularJS(1)中我们介绍了 ASP.NET MVC 捆绑和压缩、应用程序版本自动刷新和工程构建等内容。 下面介绍如何在 ASP.NET MVC 中集成 AngularJS 的第二部分。 ASP.NET 捆绑和压缩 CSS 和 JavaScript 的捆绑与压缩功能是 ASP.NET MVC 最流行和有效的特性之一。捆绑和压缩降低了 HTTP 请求和有效载荷的大小结果是可以更快和更好的执行 ASP.NET MVC 的网站。有许多可以减少 CSS 和 JavaScript 合并的大小的方法。 捆绑可以很容易地将多个文件合并或捆绑到一个文件中。您可以创建 CSSJavaScript 和其他包。压缩可以优化脚本和 CSS 代码如去除不必要的空格和注释缩短变量名到一个字符。由于捆绑和压缩降低你的 JavaScript 和 CSS 文件的大小发送的 HTTP 的字节也会显著降低。 当配置包文件时你需要考虑一个捆绑策略以及如何组织你的包文件。下面的 BundleConfig 类是内置的 ASP.NET 捆绑功能的配置文件。在 BundleConfig 类我决定通过功能模块来组织我的文件。我为工程中的每一个文件设置了一个独立的捆绑包括对脚本的单独捆绑Angular 的核心文件共享的 JavaScript 文件和主目录单客户目录和产品目录。 我创建了客户和产品目录的独立包带着这种想法当用户请求应用程序的这些源文件时应以将会动态的加载这些捆绑。由于 AngularJS 是一个纯客户端框架可以动态加载 ASP.NET 包和服务器端技术所以这两项技术相结合成为了这个要求具有发布调试模块的实例应用的最大开发挑战。 缓存与 ASP.NET 捆绑 使用 ASP.NET 捆绑的优势是它的“cache busting”的辅助方法一旦你改变了 CSS 和 JavaScript 的缓存方式这种方法将会使用自动引导的方式使捆绑的文件能够更容易的进行缓存。下面的代码示例是在一个 MVC 的 Razor 视图中执行的通常情况下是在 _Layout.cshtml 母版页。所述的 Scripts.Render 方法将会在客户端渲染并且当在非调试模式下执行时它将会产生包的虚拟路径和结束包的序列号。当你更改包的内容并重新发布你的应用程序时包将会生成一个新的版本号这有助于客户端上的浏览器缓存并生成一个新的下载包。 // _Layout.cshtmlScripts.Render(~/bundles/customers)
Scripts.Render(~/bundles/products) 该 Scripts.Render 功能是一个很好的功能但在此示例应用程序我想使用在客户端一侧动态加载的客户和产品所以我不能用渲染功能来渲染我的一些包这是挑战的开始。这个问题是以如何使用 AngularJS 从客户端 JavaScript 渲染服务器端的 ASP.NET 包开始的 _Layout.cshtml - 服务器端启动代码 一个使用 ASP.NET MVC 来引导 AngularJS 应用程序的好处是你可以通过 _Layout.cshtml 主页中服务器端的代码来加载和执行 AngularJS 的代码。这是第一步帮助解决我通过客户端代码渲染服务器端捆绑的窘境。当然你可以简单地嵌入脚本来标记客户端的代码但我需要一种方法来渲染一个包和引用并维护被追加到清除了缓存的包的目的自动版本号。 开始的时候我在 _Layout.cshtml 母版页的顶部编写了一些服务器端代码。我所做的头两件事情就是让从程序集信息类中获取应用的序列号从应用程序设置中获取检索的基本 URL。这两个都将被之后 HTML 中的 Razor 视图引擎所解析。 下面的代码段产生了我想根据需求动态加载的一些包我不想当应用启动时加载所有的前期的包。我需要的信息中的最重要一块是虚拟路径和每一次捆绑的长版本号。幸运的是访问捆绑信息的方法本身就是一种捆绑的功能。 下面的代码行的关键行引用了 BundleTable。这行代码执行了 ResolveBundleUrl 返回了该方法的虚拟路径以及每个引用的捆绑和版本号。这些代码基本上生成一个包的列表并且将该列表转换成一个 JSON 集合。后来这个 JSON 集被添加到 AngularJS。有一个 JSON 集合中的包的信息是允许从客户端 AngularJS 应用程序加载服务器端捆绑的最初的方法。 // _Layout.cshtml using CodeProject.Portal.Models { string version typeof(CodeProject.Portal.MvcApplication).Assembly.GetName().Version.ToString(); string baseUrl System.Configuration.ConfigurationManager.AppSettings[BaseUrl].ToString(); ListCustomBundle bundles new ListCustomBundle(); CodeProject.Portal.Models.CustomBundle customBundle; Liststring codeProjectBundles new Liststring(); codeProjectBundles.Add(home); codeProjectBundles.Add(customers); codeProjectBundles.Add(products); foreach (string controller in codeProjectBundles) { customBundle new CodeProject.Portal.Models.CustomBundle(); customBundle.BundleName controller; customBundle.Path BundleTable.Bundles.ResolveBundleUrl(~/bundles/ controller); customBundle.IsLoaded false; bundles.Add(customBundle); } BundleInformation bundleInformation new BundleInformation(); bundleInformation.Bundles bundles; string bundleInformationJSON Newtonsoft.Json.JsonConvert.SerializeObject( bundleInformation, Newtonsoft.Json.Formatting.None); } ASP.NET 的捆绑类有很多的功能。例如如果你想通过捆绑所有文件进行迭代你可以执行 EnumerateFiles 方法返回一个特定的包内的每个文件的虚拟路径。 foreach (var file in bundle.EnumerateFiles(new BundleContext( new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, ~/bundles/shared))) { string filePath file.IncludedVirtualPath.ToString(); } _Layout.cshtml - 标题 在 HTML 文档的标题部分有一个 RequireJS 的参考。该应用程序通过客户端 AngularJS 代码使用了 RequireJS 动态的加载包。RequireJS 是一个加载了 JavaScript API 模块的异步模块定义AMD。RequireJS 有许多功能但是对于实例应用的目的仅需要来自于 RequireJS 的请求功能以便在后面应用程序的使用。 此外Scripts.Render 和 Styles.Render 方法将在开始部分被执行。当应用程序以调试模式执行或者 EnableOptimizations 被指为 false 时渲染的方法将会在每一次捆绑中生成多个脚本。当在发布模式和启用优化时渲染方法将生成一个脚本标记来代表整个捆绑的版本戳。 这就导致了另外一个挑战那就是应用需要支持发布模式下生成捆绑脚本标签的能力和调试模式下生成独特文件的脚本标签的能力。如果你想要在调试模式下为 JavaScript 代码设置断点这点是很重要的。因为如果在发布模式下使用 JavaScript 代码的优化捆绑版本是不可能的。 最后在标题部分使用 Razor 语法的基本 URL 被早早地设定为服务器侧的基本 URL 变量。 !-- _Layout.cshtml -- !DOCTYPE html html head meta charsetutf-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 meta http-equivCache-Control contentno-cache, no-store, must-revalidate / meta http-equivPragma contentno-cache / meta http-equivExpires content0 / titleAngularJS MVC Code Project/titlev script src~/Scripts/require.js/script Scripts.Render(~/bundles/jquery) Scripts.Render(~/bundles/bootstrap) Scripts.Render(~/bundles/modernizr) Scripts.Render(~/bundles/angular) Styles.Render(~/Content/css) base href#baseUrl / /head 调试模式VS发布模式 当 EnableOptimizations 被设置为 false或者在调试模式运行时该 Scripts.Render 方法会在每一次捆绑中产生多种脚本标签。如果你想设置断点并调试 JavaScript 文件这是必要的。你有另一种选择就是在调试模式下使用 RenderFormat 方法来选人客户脚本标签。 下面的代码片段包含在 _layout.cshtml 母版页中当应用程序在调试模式下RenderFormat 会被使用。在这种模式下应用的版本序列号会被追加到捆绑中的所有JavaScript 文件的脚本标签中。对于标准的渲染脚本标签格式不包含追加版本号来说这也算是个小弥补。 从 Visual Studio 中启动应用程序时您可能会遇到浏览器缓存的问题。同时也可能会花时间来猜测你运行的是否是最新版本的 JavaScript 文件。在浏览器中按 F5 可以解决这个问题。为了避免这个问题一起发生应用程序版本号会被附加到脚本标签中。使用自动版本插件版本号会在每次构建中自动递增。使用这项技术我能够知道每一次的编译和运行使用的是 JavaScript 文件的最新版本这为我省了很多时间。 // _Layout.cshtml if (HttpContext.Current.IsDebuggingEnabled) { Scripts.RenderFormat(script type\text/javascript\ src\{0}?ver version \ /script, ~/bundles/shared) Scripts.RenderFormat(script type\text/javascript\ src\{0}?ver version \ /script,~/bundles/routing-debug) } else { Scripts.Render(~/bundles/shared) Scripts.Render(~/bundles/routing-production) } 服务器端 Razor 数据和 AngularJS 之间的桥梁 现在我已经创建了服务器端的捆绑数据的收集接下来的挑战就是注入并创建服务器端和客户端 AngularJS 代码的桥梁。在 _Layout.cshtml 母版页我创建了能够创造一个 AngularJS 供应商的匿名的 JavaScript 功能。最初我计划创建一个常规的 AngularJS 服务或者一个包含在 _Layout.cshtml 文件中能够使用 Razor 语法注入服务器端的方法集。 不幸的是直到 AngularJS 配置阶段完成之后才能提供 AngularJS 服务和方法集因此我无法在主页中创建一个没有 AngularJS 错误的服务。为了克服这个限制则需要创建一个 AngularJS 的提供者。提供者的功能是能够创建提供方法集和服务的实例。提供者允许你在 Angular 配置过程中创建和配置一个服务。 服务提供者名称是以他们所提供工作的提供商为开始的。下面的代码片段中代码创建一个“applicationConfiguration”提供商这个提供商正在被 applicationConfigurationProvider 引用。这个提供商将会在构造函数中被配置来设定用于动态请求的应用所需的程序集版本号和捆绑列表。MVC Razor 代码在构造函数中会注入服务器端的数据。 // _Layout.cshtml (function () { var codeProjectApplication angular.module(codeProject); codeProjectApplication.provider(applicationConfiguration, function () { var _version; var _bundles; return { setVersion: function (version) { _version version; }, setBundles: function (bundles) { _bundles bundles; }, getVersion: function () { return _version; }, getBundles: function () { return _bundles; }, $get: function () { return { version: _version, bundles: _bundles } } } }); codeProjectApplication.config(function (applicationConfigurationProvider) { applicationConfigurationProvider.setVersion(version); applicationConfigurationProvider.setBundles(Html.Raw(bundleInformationJSON)); }); })(); 路由产生和动态加载 MVC 捆绑 现在你可能已经看到了很多例子实现了每个内容页硬编码路径的 AngularJS 示例。示例应用程序的路由使用基于约定的方法这种方法允许路由表使用硬编码的路由方法来实现使用基于约定的方法。所有的内容页和相关联的 JavaScript 文件将会遵循命名约定规则这个规则允许该应用程序来解析路由并动态地确定每个内容页需要哪些 JavaScript 文件。 下面的示例应用程序的路由表只需要分析出三条路线 一个用于根路径/一个标准路由路径如/:section/:tree包含路由参数的路由如/:section/:tree/:id 我决定从 ASP.NET 捆绑中加载 JavaScript 文件下面的路由配置代码需要包含一些 applicationConfigurationProvider 引用的代码来用于创建保存之前的捆绑信息。捆绑信息将会被解析为 JSON 集。捆绑信息集将会用于返回虚拟的捆绑路径。此外JSON 集将被用于跟踪被加载的捆绑。一旦捆绑被加载就不需要第二次捆绑了。 有几件事情需要写入路由代码中。首先每当用户选择一个页面来加载一定功能模块时对于模块绑定的所有 JavaScript 文件需要被下载。例如当用户选择客户模式中的一个内容页面时以下的代码会查看模块的捆绑是否已经通过 JSON _bundles collection 的 isLoaded 属性被检查了并且如果 isLoaded 为 false则捆绑将会被记载 isLoaded 属性会被设置为 true。 当确定需要下载哪些模式的捆绑时有两件事情需要去加载捆绑deferred promise 和 RequireJS。deferred promise 可以帮助你异步运行函数当它完成执行就会返回。 现在最后一块本文之谜是确定从客户端代码包中加载的方式。我在以前的文章 CodeProject.com 使用 RequireJS前面提到的来动态加载 JavaScript 文件我使用捆绑来加载 RequireJS。使用 RequireJS“需求”的功能 我通过捆绑的虚拟路径进入需求功能。事实证明需求功能将会加载任何能够更好执行捆绑加载的路径。 当我第一次使用 RequireJS 的路径来下载捆绑时我已经完成了 RequireJS 和它的所有配置。事实证明我能够去掉这一切只是简单地加载 RequireJS 库并使用它的需求功能。我甚至没有使用 RequireJS 定义表述来预安装我的动态加载控制器。很多试验和错误之后我已经达到了本文的目的。我现在可以通过客户端代码加载服务器端的捆绑。 // CodeProjectRouting-production.js angular.module(codeProject).config( [$routeProvider, $locationProvider, applicationConfigurationProvider function ($routeProvider, $locationProvider, applicationConfigurationProvider) { var baseSiteUrlPath $(base).first().attr(href); var _bundles JSON.parse(applicationConfigurationProvider.getBundles()); this.getApplicationVersion function () { var applicationVersion applicationConfigurationProvider.getVersion(); return applicationVersion; } this.getBundle function (bundleName) { for (var i 0; i _bundles.Bundles.length; i) { if (bundleName.toLowerCase() _bundles.Bundles[i].BundleName) { return _bundles.Bundles[i].Path; } } } this.isLoaded function (bundleName) { for (var i 0; i _bundles.Bundles.length; i) { if (bundleName.toLowerCase() _bundles.Bundles[i].BundleName) { return _bundles.Bundles[i].IsLoaded; } } } this.setIsLoaded function (bundleName) { for (var i 0; i _bundles.length; i) { if (bundleName.toLowerCase() _bundles.Bundles[i].BundleName) { _bundles.Bundles[i].IsLoaded true; break; } } } $routeProvider.when(/:section/:tree, { templateUrl: function (rp) { return baseSiteUrlPath views/ rp.section / rp.tree .html?v this.getApplicationVersion(); }, resolve: { load: [$q, $rootScope, $location, function ($q, $rootScope, $location) { var path $location.path().split(/); var parentPath path[1]; var bundle this.getBundle(parentPath); var isBundleLoaded this.isLoaded(parentPath); if (isBundleLoaded false) { this.setIsLoaded(parentPath); var deferred $q.defer(); require([bundle], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; } }] } }); $routeProvider.when(/:section/:tree/:id, { templateUrl: function (rp) { return baseSiteUrlPath views/ rp.section / rp.tree .html?v this.getApplicationVersion(); }, resolve: { load: [$q, $rootScope, $location, function ($q, $rootScope, $location) { var path $location.path().split(/); var parentPath path[1]; var bundle this.getBundle(parentPath); var isBundleLoaded this.isLoaded(parentPath); if (isBundleLoaded false) { this.setIsLoaded(parentPath); var deferred $q.defer(); require([bundle], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; } }] } }); $routeProvider.when(/, { templateUrl: function (rp) { return baseSiteUrlPath views/Home/Index.html?v this.getApplicationVersion(); }, resolve: { load: [$q, $rootScope, $location, function ($q, $rootScope, $location) { var bundle this.getBundle(home); var isBundleLoaded this.isLoaded(home); if (isBundleLoaded false) { this.setIsLoaded(home); var deferred $q.defer(); require([bundle], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; } }] } }); $locationProvider.html5Mode(true); } ]); 以上是如何在 ASP.NET MVC 中集成 AngularJS 的第二部分内容最后一篇内容会在明天呈现敬请期待 文章来源By Mark J. Caplin 原文链接http://www.codeproject.com/Articles/1033076/Integrating-AngularJS-with-ASP-NET-MVC .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注