Javascript 如何在不重新编译的情况下使用ASP.NET捆绑和缩小?

Javascript 如何在不重新编译的情况下使用ASP.NET捆绑和缩小?,javascript,asp.net,bundling-and-minification,Javascript,Asp.net,Bundling And Minification,限制:我没有使用MVC,只是在我的web应用程序中使用普通的ol'.aspx文件。也不使用母版页-每个页面都是不同的野兽,所以这种解决方案不适合我 我读过的关于捆绑和缩小的大多数示例都需要一些特殊的MVC标记,或者要求您预先识别捆绑的脚本/样式表,然后参考这些捆绑包。我希望避免每次在.aspx页面中添加或修改.js引用时重新编译DLL 阅读Msft的文档让我有点不知所措。。是否有一种方法(如ASP.NET控件)可以只包装一系列脚本标记(或CSS的链接标记)来动态创建和使用捆绑包?我不想重新发明轮

限制:我没有使用MVC,只是在我的web应用程序中使用普通的ol'.aspx文件。也不使用母版页-每个页面都是不同的野兽,所以这种解决方案不适合我

我读过的关于捆绑和缩小的大多数示例都需要一些特殊的MVC标记,或者要求您预先识别捆绑的脚本/样式表,然后参考这些捆绑包。我希望避免每次在.aspx页面中添加或修改.js引用时重新编译DLL

阅读Msft的文档让我有点不知所措。。是否有一种方法(如ASP.NET控件)可以只包装一系列
脚本
标记(或CSS的
链接
标记)来动态创建和使用捆绑包?我不想重新发明轮子,但认真考虑创建自己的用户控件/自定义控件来处理这个问题。还有其他选择吗

例如,查找类似以下内容:

<asp:AdHocScriptBundle id="mypage_bundle" runat="server">
    <script type="text/javascript" src="~/scripts/mypage1.js"></script>
    <script type="text/javascript" src="~/scripts/mypage2.js"></script>
    <script type="text/javascript" src="~/scripts/mypage3.js"></script>
</asp:AdHocScriptBundle>
<script type="text/javascript" src="/webappname/scripts/mypage1.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage2.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage3.js"></script>
当绑定被禁用时,输出内容通常如下所示:

<asp:AdHocScriptBundle id="mypage_bundle" runat="server">
    <script type="text/javascript" src="~/scripts/mypage1.js"></script>
    <script type="text/javascript" src="~/scripts/mypage2.js"></script>
    <script type="text/javascript" src="~/scripts/mypage3.js"></script>
</asp:AdHocScriptBundle>
<script type="text/javascript" src="/webappname/scripts/mypage1.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage2.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage3.js"></script>

有什么想法吗


无论如何,我将推出自己的,但如果已经有一个解决方案,请分享,谢谢

使用ASP.NET中的默认绑定/缩小功能,这是不可能的。捆绑的整个要点是创建一个to文件,以减少加载静态文件(如.JS和.CSS文件)的浏览器请求数

滚动你自己的是你唯一的选择。但是,请注意,每行
都会导致浏览器请求。因为,您可以有等待时间来加载这些静态文件


仅供参考,您不必每次使用内置绑定更新.JS文件时都重新编译DLL。您只需重置运行应用程序的应用程序池即可。如果您使用外部会话持久性模型运行,您的用户将不会注意到何时会发生这种情况。

您的问题在于您没有真正考虑这个问题。如果你是,你会意识到你所要求的是行不通的

为什么??因为脚本标记需要生成指向不同url的外部链接引用。因此,您在当前文件头中放置的任何内容都不会影响实际包含捆绑包的其他URL。因此,无法在页面本身中动态更改捆绑包,因为根据定义,捆绑包必须在外部资源中定义

现在,没有什么规定必须在您自己的解决方案中将这些捆绑包编译成DLL,但它们不能嵌入当前呈现的页面中


您可能需要研究一些基于javascript的缩小工具,因为它们通常没有编译。

我推出了自己的解决方案,效果非常好!我创建了4个可以用作自定义服务器控件的类:

  • 脚本包
  • 剧本
  • 样式包
  • 链接
这些调用函数围绕着我的自定义绑定库,它本身就是System.Web.Optimization API的包装器

在呈现
ScriptBundle
StyleBundle
的过程中,我会检查一个内部设置(与我在System.Web.Optimization API中设置
启用优化
的设置相同),该设置告诉页面要么使用绑定,要么简单地写出正常的
脚本
/
链接
标记。如果绑定已启用,它将从我的自定义绑定库调用此函数(对于脚本,下面代码中类似的样式代码tho.
Bundler
是我的自定义绑定库的类-以防Microsoft更改System.Web.Optimization API我希望在两者之间添加一个层,这样我就不必对代码进行太多更改):

为了确保仅在捆绑包不存在时创建捆绑包,我首先使用此方法检查捆绑包(在使用上述方法之前):

然后,我使用此函数通过System.Web.Optimization将URL吐出到捆绑包:

    public static System.Web.IHtmlString GetScriptBundleHTML(string virtualTargetPath)
    {
        return System.Web.Optimization.Scripts.Render(virtualTargetPath);
    }
在.aspx文件中,我执行以下操作:

<%@ Register TagPrefix="cc1" Namespace="AdHocBundler" Assembly="AdHocBundler" %>

类似的问题,但有不同的(非web控件)解决方案:我完全不同意。请看我上面的答案。嗯,有很多反对者,但不管他们是否相信我,我的解决方案工作得很好,只需要对.aspx文件进行最少的代码更改。此外,由于这是一个数据密集的企业web应用程序,所以缩小是我最不关心的问题。我以前应该这么说,但我最关心的是当.js文件的内容发生变化时,让用户的浏览器自动更新缓存。(我之前没有提到的另一个目标是使现有的
标记的转换尽可能简单,因为这个web应用程序有数百个.aspx文件,我必须培训其他开发人员如何进行转换。)@没什么必要-换句话说,你不想实现web优化的任何目标。。但却坚持你可以做一些你实际上没有做的事情。我很高兴你的解决方案对你有效,但这不是你所要求的。我不确定我是否遵循。我得到了(1)缩小(最初发送的数据更少),(2)自动缓存更新(由于文件内容的散列),以及(3)缓存更新期间的性能改进(由于捆绑多个脚本文件以克服浏览器对单个域并发请求的限制)。缩小对我来说是低优先级的,因为许多页面都是数据密集型的,通常使用AJAX来请求数据块(.js文件的大小与请求的XML/Json数据的大小成比例很小)。最高优先级是让缓存在.js文件更改时自动更新(比如升级后)。这是可能的,我做到了。见上面我的答案。我明白你的意思,重新编译不是必需的,我应该说得更具体一些——我以前之所以要这么做,是因为我选择在Global.asax.cs中启动应用程序时创建“核心”包(每页都使用脚本),但我现在意识到这并不是绝对必要的——谢谢。
<%@ Register TagPrefix="cc1" Namespace="AdHocBundler" Assembly="AdHocBundler" %>
<cc1:ScriptBundle name="MyBundle" runat="Server">
    <cc1:script src='~/js/script1.js'/>
    <cc1:script src='~/js/utils/script2.js'/>
</cc1:ScriptBundle>
[DefaultProperty("Name")]
[ParseChildren(true, DefaultProperty = "Scripts")]
public class ScriptBundle : Control
{
    public ScriptBundle()
    {
        this.Enabled = true;
        this.Scripts = new List<Script>();
    }

    [PersistenceMode(PersistenceMode.Attribute)]
    public String Name { get; set; }

    [PersistenceMode(PersistenceMode.Attribute)]
    [DefaultValue(true)]
    public Boolean Enabled { get; set; }

    [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public List<Script> Scripts { get; set; }

    protected override void Render(HtmlTextWriter writer)
    {
        if (String.IsNullOrEmpty(this.Name))
        {
            // Name is used to generate the bundle; tell dev if he forgot it
            throw new Exception("ScriptBundle Name is not defined.");
        }

        writer.BeginRender();

        if (this.Enabled && Bundler.EnableOptimizations)
        {
            if (this.Scripts.Count > 0)
            {
                string bundleName = String.Format("~/bundles{0}/{1}.js",
                    HttpContext.Current.Request.FilePath,
                    this.Name).ToLower();

                // create a bundle if not exists
                if (!Bundler.BundleExists(bundleName))
                {
                    string[] scriptPaths = new string[this.Scripts.Count];
                    int len = scriptPaths.Length;
                    for (int i = 0; i < len; i++)
                    {
                        if (!string.IsNullOrEmpty(this.Scripts[i].Src))
                        {
                            // no need for resolve client URL here - bundler already does it for us, so paths like "~/scripts" will already be expanded
                            scriptPaths[i] = this.Scripts[i].Src;
                        }
                    }
                    Bundler.AddScriptBundle(bundleName, scriptPaths);
                }

                // spit out a reference to bundle
                writer.Write(Bundler.GetScriptBundleHTML(bundleName));
            }
        }
        else
        {
            // do not use bundling. generate normal script tags for each Script
            foreach (Script s in this.Scripts)
            {
                if (!string.IsNullOrEmpty(s.Src))
                {
                    // render <script type='<type>' src='<src'>/> ... and resolve URL to expand tilde, which lets us use paths relative to app root
                    // calling writer.Write() directly since it has less overhead than using RenderBeginTag(), etc., assumption is no special/weird chars in the cc1:script attrs
                    writer.Write(String.Format(Script.TAG_FORMAT_DEFAULT,
                        s.Type,
                        Page.ResolveClientUrl(s.Src)));
                }
            }
        }
        writer.EndRender();
    }
}

public class Script
{
    public const String ATTR_TYPE_DEFAULT = "text/javascript";
    public const String TAG_FORMAT_DEFAULT = "<script type=\"{0}\" src=\"{1}\"></script>";

    public Script()
    {
        this.Type = ATTR_TYPE_DEFAULT;
        this.Src = null;
    }

    public String Type { get; set; }
    public String Src { get; set; }
    public String Language { get; set; }
}