运行时动态捆绑和缩小文件在MVC 4(Runtime dynamic bundling and mi

2019-07-29 10:31发布

我想知道是否有人能帮助我捆绑,并使用附带MVC 4.我有一个多租户应用程序中,我要决定哪些JS文件应根据每个用户的设置来加载新的优化命名空间涅槃。 一种方法是创建所有包的前期和改变根据用户的设置resolvebundleurl的虚拟路径,但感觉没有真正的正确方法。 我也有基于用户设置,我想在运行时已经过压缩一个CSHTML视图动态CSS。

有什么建议? 我也看到了很多其他问题的反应退房Requestreduce的,但他们都是来自同一用户。

什么是处理这两种情况下,最好的办法?

提前致谢!

Answer 1:

你可以采取一种方法是动态生成的捆绑应用程序启动时。 所以,如果你的脚本位于~/scripts ,你可以这样做:

Bundle bundle = new Bundle("~/scripts/js", new JsMinify());

if (includeJquery == true) {     
  bundle.IncludeDirectory("~/scripts", "jquery-*");
  bundle.IncludeDirectory("~/scripts", "jquery-ui*");
} 

if (includeAwesomenes == true) {
  bundle.IncludeDirectory("~/scripts", "awesomeness.js");
}

BundleTable.Bundles.Add(bundle);

然后,您的标记可以是这样的

@Scripts.Render("~/Scripts/Libs/js")

注:我使用的是最新的NuGet包位于system.web.optimization(现Microsoft.AspNet.Web.Optimization) 这里 。 斯科特Hanselman有一个很好的职位了。



Answer 2:

我写了一个辅助函数来动态缩小我的CSS和JS

    public static IHtmlString RenderStyles(this HtmlHelper helper, params string[] additionalPaths)
    {
        var page = helper.ViewDataContainer as WebPageExecutingBase;
        if (page != null && page.VirtualPath.StartsWith("~/"))
        {
            var virtualPath = "~/bundles" + page.VirtualPath.Substring(1);
            if (BundleTable.Bundles.GetBundleFor(virtualPath) == null)
            {
                var defaultPath = page.VirtualPath + ".css";
                BundleTable.Bundles.Add(new StyleBundle(virtualPath).Include(defaultPath).Include(additionalPaths));
            }
            return MvcHtmlString.Create(@"<link href=""" + HttpUtility.HtmlAttributeEncode(BundleTable.Bundles.ResolveBundleUrl(virtualPath)) + @""" rel=""stylesheet""/>");
        }
        return MvcHtmlString.Empty;
    }

    public static IHtmlString RenderScripts(this HtmlHelper helper, params string[] additionalPaths)
    {
        var page = helper.ViewDataContainer as WebPageExecutingBase;
        if (page != null && page.VirtualPath.StartsWith("~/"))
        {
            var virtualPath = "~/bundles" + page.VirtualPath.Substring(1);
            if (BundleTable.Bundles.GetBundleFor(virtualPath) == null)
            {
                var defaultPath = page.VirtualPath + ".js";
                BundleTable.Bundles.Add(new ScriptBundle(virtualPath).Include(defaultPath).Include(additionalPaths));
            }
            return MvcHtmlString.Create(@"<script src=""" + HttpUtility.HtmlAttributeEncode(BundleTable.Bundles.ResolveBundleUrl(virtualPath)) + @"""></script>");
        }
        return MvcHtmlString.Empty;
    }

用法

〜/视图/主页/ Test1.cshtml

〜/浏览/首页/ Test1.cshtml.css

〜/浏览/首页/ Test1.cshtml.js

在Test1.cshtml

@model object
@{
   // init
}@{

}@section MainContent {
  {<div>@{
     if ("work" != "fun")
     {
        {<hr/>}
     }
  }</div>}
}@{

}@section Scripts {@{
  {@Html.RenderScripts()}
}@{

}@section Styles {@{
  {@Html.RenderStyles()}
}}

但ofcoz,我把我的大部分sripts的,样式在〜/脚本/ .js文件,〜/内容/的CSS

在Appp_Start进行注册



Answer 3:

我们认为早在支持动态视频集,但这种做法的根本问题是多服务器方案(即云)将无法正常工作。 如果所有的包都没有预先定义,被发送到不同的服务器比服务的页面请求将获得404响应(如捆绑定义将只对处理的页面请求服务器存在)的任何一个捆绑请求。 因此,我建议建立全包了前面,这是主线场景。 束的动态配置可能工作为好,但不是完全支持的方案。



Answer 4:

更新:不知道它的问题,但我使用MVC 5.2.3和Visual Studio 2015年,问题是有点老了。

不过,我提出,在_viewStart.cshtml工作动态捆绑。 我所做的是我做的,存储在捆束的字典一个辅助类。 然后,在应用程序启动我拉他们从字典和注册。 我做了一个静态boolen“bundlesInitialzed”,使束仅添加到字典一次。

例如助手:

public static class KBApplicationCore: .....
{
    private static Dictionary<string, Bundle> _bundleDictionary = new Dictionary<string, Bundle>();
    public static bool BundlesFinalized { get { return _BundlesFinalized; } }
    /// <summary>
    /// Add a bundle to the bundle dictionary
    /// </summary>
    /// <param name="bundle"></param>
    /// <returns></returns>
    public static bool RegisterBundle(Bundle bundle)
    {
        if (bundle == null)
            throw new ArgumentNullException("bundle");
        if (_BundlesFinalized)
            throw new InvalidOperationException("The bundles have been finalized and frozen, you can only finalize the bundles once as an app pool recycle is needed to change the bundles afterwards!");
        if (_bundleDictionary.ContainsKey(bundle.Path))
            return false;
        _bundleDictionary.Add(bundle.Path, bundle);
        return true;
    }
    /// <summary>
    /// Finalize the bundles, which commits them to the BundleTable.Bundles collection, respects the web.config's debug setting for optimizations
    /// </summary>
    public static void FinalizeBundles()
    {
        FinalizeBundles(null);
    }
    /// <summary>
    /// Finalize the bundles, which commits them to the BundleTable.Bundles collection
    /// </summary>
    /// <param name="forceMinimize">Null = Respect web.config debug setting, True force minification regardless of web.config, False force no minification regardless of web.config</param>
    public static void FinalizeBundles(bool? forceMinimize)
    {
        var bundles = BundleTable.Bundles;
        foreach (var bundle in _bundleDictionary.Values)
        {
            bundles.Add(bundle);
        }
        if (forceMinimize != null)
            BundleTable.EnableOptimizations = forceMinimize.Value;
        _BundlesFinalized = true;
    }        
}

例如_ViewStart.cshtml

@{

    var bundles = BundleTable.Bundles;
    var baseUrl = string.Concat("~/App_Plugins/", KBApplicationCore.PackageManifest.FolderName, "/");
    //Maybe there is a better way to do this, the goal is to make the bundle configurable without having to recompile the code
    if (!KBApplicationCore.BundlesFinalized)
    {
        //Note, you need to reset the application pool in order for any changes here to be reloaded as the BundlesFinalized property is a static field that will only reset to false when the app restarts.
        Bundle mainScripts = new ScriptBundle("~/bundles/scripts/main.js");
        mainScripts.Include(new string[] {
            baseUrl + "Assets/lib/jquery/jquery.js",
            baseUrl + "Assets/lib/jquery/plugins/jqcloud/jqcloud.js",
            baseUrl + "Assets/lib/bootstrap/js/bootstrap.js",            
            baseUrl + "Assets/lib/bootstrap/plugins/treeview/bootstrap-treeview.js",   
            baseUrl + "Assets/lib/angular/angular.js",
            baseUrl + "Assets/lib/ckEditor/ckEditor.js"      
        });
        KBApplicationCore.RegisterBundle(mainScripts);

        Bundle appScripts = new ScriptBundle("~/bundles/scripts/app.js");
        appScripts.Include(new string[] {
            baseUrl + "Assets/app/app.js",
            baseUrl + "Assets/app/services/*.js",
            baseUrl + "Assets/app/directives/*.js",
            baseUrl + "Assets/app/controllers/*.js"
        });
        KBApplicationCore.RegisterBundle(appScripts);

        Bundle mainStyles = new StyleBundle("~/bundles/styles/main.css");
        mainStyles.Include(new string[] {
           baseUrl + "Assets/lib/bootstrap/build/less/bootstrap.less",
           baseUrl + "Assets/lib/bootstrap/plugins/treeview/bootstrap-treeview.css",   
           baseUrl + "Assets/lib/ckeditor/contents.css",
           baseUrl + "Assets/lib/font-awesome/less/font-awesome.less",
           baseUrl + "Assets/styles/tlckb.less"
        });
        mainStyles.Transforms.Add(new BundleTransformer.Core.Transformers.CssTransformer());
        mainStyles.Transforms.Add(new CssMinify());
        mainStyles.Orderer = new BundleTransformer.Core.Orderers.NullOrderer();
        KBApplicationCore.RegisterBundle(mainStyles);


        KBApplicationCore.FinalizeBundles(true); //true = Force Optimizations, false = Force non Optmizations, null = respect web.config which is the same as calling the parameterless constructor.
    }
}

注:本应更新为使用线程锁来防止2项请求第一个退出之前进入包代码。

这种工作方式是启动上的应用程序池重置后的第一个请求到现场运行图。 它呼吁帮助的RegisterBundle并传递ScriptBundle或StyleBundle在顺序RegisterBundles字典被调用。

当FinalizeBundles是叫你可以指定True这将迫使优化,无论web.config中的调试设置,或将其空或使用构造,而不该参数将它尊重web.config中设置。 传递false将强制使用,即使调试是真的没有优化。 FinalizeBundles登录在束表,并设置的_BundlesFinalized为true束。

一旦最后确定,企图再次调用RegisterBundle会抛出异常,它在这一点上冻结。

此设置允许您添加新的包,查看启动和重置应用程序池,让他们才会生效。 我写这个的初衷是因为我做的东西别人会用,所以我希望他们能够彻底改变前端用户界面,而无需重建源改变束。



文章来源: Runtime dynamic bundling and minifying in MVC 4