jNs 在 ASP.NET MVC 项目中的应用
最近做项目用到ASP.NET Web Optimizatoin Framework,发现Sea.js的依赖加载在 Release 版本下不能很好的工作了——因为 Web.Optimizatoin 合并了所有脚本。同时由于写惯了 Java
程序和 C# 程序,对于没有命名空间概念的Sea.js
和RequireJS
也感觉不爽。考虑了下,觉得模块管理其实并不复杂,所以将之前在《ASP.NET MVC4 捆绑(Bundle)技术下的 JavaScript》中提到的js-modular
的基础上进行了改进,最终产生了jNs。
jNs 是一个具有命名空间概念的 JavaScript 模块管理工具。与Sea.js和ReqireJS等模块管理工具不同,jNs 只管理模块的定义和使用,而不负责加载,非常适合发布合并 JavaScript 代码的 Web 项目,比如使用了ASP.NET Web Optimization Framework提供的 Script Bundle 功能的 ASP.NET 项目,以及使用UglifyJS压缩合并脚本的项目等。
jNs托管在git.oschina.net上,其 README 中已经有详情的示例,所以这里就不废话了。这里主要说说在 ASP.NET MVC 项目里怎么使用。
第一,定义合理的脚本结构下面展示了一个来自某个实际项目的脚本目录(有所精简),也许不是最好的结构,但我个人觉得很清晰(用方括号[]
括起来的是目录)。
[Scripts]│-jNs-1.0.0.js│-jNs-1.0.0.min.js│-jquery-2.1.3.js│-jquery-2.1.3.min.js├─[core]│└─[co]││-app.js││-compatible.js││-util.js│├─[app]│││-ajax.js│││-dialog.js│││-page.js││└-ui.js│└─[util]││-case.js││-debug.js│└-format.js└─[page]├─[home]│└-index.js└─[user]└-index.js
其中有两个比较关键的主目录,一个是core
,一个是page
。
core
是整个项目中需要使用到的模块,在bunles
中配置成一个名为~/bundle/core
的ScriptBundle
。Release 版本下会被打包成一个合并的脚本文件。大部分的页面只需要引用~/bundles/core
就可以了,因为大部分的公用逻辑都在这里了。
但是仍然会有一部分页面比较特殊,需要有自己的脚本。那么这些脚本就按一定的路径保存在page
目录下。
core
中的目录结构与模块的结构对应,这样查找脚本文件的时候就比较方便,比如示例中的结构对应的模块全称(含命名空间)就是:
co.appco.app.ajaxco.app.dialogco.app.pageco.app.uico.compatibleco.utilco.util.caseco.util.debugco.util.format
这个结构看起来就像 Java 一样。不过与 Java 不同,目录不必与命名空间(或包)对应,文件名也不必与模块名相同——这似乎更像 C#。
还有一点需要注意的是,在同一个命名空间下,子级命名空间和模块名是可以相同的,这也算是 jNs 的特点之一吧。
有了合理的结构,还需要解决下面这个问题。
第二,保证 jNs.js 在模块定义之前加载虽然一般认为在ScriptBunlde
中Include
的脚本会顺序加载,或者说在 Release 版本下按顺序合并,所以认为可以这样写配置:
//BundleConfig.cs//注意:这是错误的示例publicstaticvoidRegisterBundles(BundleCollectionbundles){bunles.Add(newScriptBundle("~/bundles/core").Include("~/scripts/jNs-{version}.js")..IncludeDirectory("~/scripts/core/","*.js",true));}
可惜事实不是。我阅读了它的源码,发现它使用了一个Dictionary
来进行中间过程的处理,所以最终输出的顺序完全是不确定的。所以 jNs 有可能在某个模块文件之后加载,那么模块文件中的jNs(...)
就会出错——因为还没定义。
不过很幸运,Web.Optimization 提供了IBundleOrderer
接口来处理顺序的问题。我比较懒,所以不想直接去实现IBundleOrderer
接口,而是从DefaultBundleOrderer
继承了个子类来处理顺序——不管DefaultBundleOrderer
是怎么处理的,我只需要在它处理之后按输入的顺序重新排列一下就好了
publicclassAsIsBundleOrderer:DefaultBundleOrderer{publicoverrideIEnumerable<BundleFile>OrderFiles(BundleContextcontext,IEnumerable<BundleFile>files){varoriginalList=files.ToList();IEnumerable<BundleFile>orderFiles=base.OrderFiles(context,originalList);returnorderFiles.OrderBy(f=>originalList.IndexOf(f));}}
在有AsIsBundleOrderer
之后,再来看看正确的 Bundle 配置
publicstaticvoidRegisterBundles(BundleCollectionbundles){varbundle=newScriptBundle("~/bundles/core").Include("~/Scripts/jNs-{version}.js").IncludeDirectory("~/Scripts/core/","*.js",true);bundle.Orderer=newAsIsBundleOrder();bundles.Add(bundle);}
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。