19

MVC 4 に同梱されている新しい最適化名前空間を使用してバンドルと縮小を手伝ってくれる人がいるかどうか疑問に思っていました。ユーザーごとの設定に基づいてどの js ファイルをロードするかを決定したいマルチテナント アプリケーションがあります。1 つのアプローチは、事前にすべてのバンドルを作成し、ユーザーの設定に基づいて resolvebundleurl の仮想パスを変更することですが、それは実際には正しい方法ではありません。また、ユーザー設定に基づいて cshtml ビューに動的な css があり、実行時に縮小したいと考えています。

助言がありますか?Requestreduce をチェックする他の質問でも多くの反応が見られますが、それらはすべて同じユーザーからのものです。

両方の状況を処理するための最良のアプローチは何でしょうか?

前もって感謝します!

4

4 に答える 4

12

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")

注:ここにある system.web.optimization (現在は Microsoft.AspNet.Web.Optimization) の最新の nuget パッケージを使用しています。Scott Hanselman はそれについて良い記事を書いています。

于 2012-06-02T04:09:00.033 に答える
9

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;
    }

利用方法

~/views/Home/Test1.cshtml

~/Views/Home/Test1.cshtml.css

~/Views/Home/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、私はほとんどのスクリプト、スタイルを ~/Scripts/ .js、~/Content/ .cssに入れました

それらをAppp_Startに登録します

于 2014-07-26T06:57:25.100 に答える
5

早い段階で動的バンドルのサポートを検討しましたが、そのアプローチの根本的な問題は、マルチ サーバー シナリオ (つまりクラウド) が機能しないことです。すべてのバンドルが事前に定義されていない場合、ページ要求を処理したサーバーとは異なるサーバーに送信されたバンドル要求は、404 応答を受け取ります (バンドル定義は、ページ要求を処理したサーバーにのみ存在するため)。その結果、すべてのバンドルを前もって作成することをお勧めします。これがメインライン シナリオです。バンドルの動的構成も同様に機能する可能性がありますが、それは完全にサポートされているシナリオではありません。

于 2012-06-15T23:22:15.083 に答える
0

更新: 問題があるかどうかはわかりませんが、MVC 5.2.3 と Visual Studio 2015 を使用しています。質問は少し古いです。

ただし、_viewStart.cshtml で機能する動的バンドルを作成しました。私がしたことは、バンドルの辞書にバンドルを格納するヘルパー クラスを作成することでした。次に、アプリの起動時に、それらを辞書から取得して登録します。そして、バンドルが辞書に一度だけ追加されるように、静的ブーレン「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 を呼び出し、RegisterBundle が呼び出された順序で ScriptBundle または StyleBundle をディクショナリに渡します。

FinalizeBundles が呼び出されたときに、web.config デバッグ設定に関係なく最適化を強制する True を指定するか、null のままにするか、そのパラメーターなしでコンストラクターを使用して、web.config 設定を尊重させることができます。false を渡すと、debug が true であっても最適化を使用しないように強制されます。FinalizeBundles バンドル テーブルにバンドルを登録し、_BundlesFinalized を true に設定します。

一度終了すると、RegisterBundle を再度呼び出そうとすると例外がスローされ、その時点で凍結されます。

この設定により、新しいバンドルを追加してアプリケーション プールを表示およびリセットし、それらを有効にすることができます。私がこれを書いていた最初の目標は、他の人が使用するものを作成しているので、ソースを再構築してバンドルを変更することなく、フロントエンド UI を完全に変更できるようにすることでした。

于 2015-08-27T00:43:16.237 に答える