Deklaratywne dynamiczne wiązanie / minifikacja dla MVC?

Czy istnieje sposób, aby pozwolić mi zadeklarować stan magazynowy lub wtyczkowy (jak pakiet NuGet).js, .cssi idealnie.less pliki w widokach i częściach MVC, w których ich używam, i czy mają automatycznie pakowane pakiety uruchomieniowe i minify w produkcji? (znany jako. "Autobunding„)

Próbowałem wbudowanego pakietu MVC 4. Nie podoba mi się, że pakiety są definiowane z dala od miejsca, w którym autor strony mógłby je znaleźćBundleConfig.cs . Jest to niewykonalne dla członków zespołu innych niż C #.

Tak jakprzykład tego, czego szukam, oto, co ja sam użyłemSquishIt.


/// <summary>
/// Caches a bundle of .js and/or .css specific to this ViewPage, at a path similar to:
/// shared_signinpartial_F3BD3CCE1DFCEA70F5524C57164EB48E.js
/// </summary>
public abstract class ExtendedViewPage<TModel> : WebViewPage<TModel> {
    // This is where I keep my assets, and since I don't actually store any in my root,
    // I emit all my bundles here. I also use the the web deployment engine,
    // and remove extra files on publish, so I never personally have to clean them up,
    // and I also don't have to hand-identify generated bundles from original code.
    // However, to keep from needing to give the app write permissions
    // on a static content folder, or collocate bundles with original assets,
    // or conform to a specific asset path, this should surely be configurable
    private const string ASSET_PATH = "~/assets/";

    /// <summary>
    /// Emits here the bundled resources declared with "AddResources" on all child controls
    /// </summary>
    public MvcHtmlString ResourceLinks {
        get {
            return MvcHtmlString.Create(
                string.Join("", CssResourceLinks) + string.Join("", JsResourceLinks));

    // This allows all resources to be specified in a single command,
    // which permits .css and .js resources to be declared in an
    // interwoven manner, in any order the site author prefers
    // For me, this makes it clearer, to group my related .css and .js links,
    // and to place my often control-specific CSS near last in the list
    /// <summary>
    /// Queues compressible resources to be emitted with the ResourceLinks directive
    /// </summary>
    /// <param name="resourceFiles">Project paths to JavaScript and/or CSS files</param>
    public void AddResources(params string[] resourceFiles) {
        var css = FilterFileExtension(resourceFiles, ".css");
        var js = FilterFileExtension(resourceFiles, ".js");

    /// <summary>
    /// Bundles JavaScript files to be emitted with the ResourceLinks directive
    /// </summary>
    /// <param name="resourceFiles">Zero or more project paths to JavaScript files</param>
    public void AddJsResources(params string[] resourceFiles) {
        if (resourceFiles.Any()) {
            JavaScriptBundle jsBundle = Bundle.JavaScript();
            foreach (string jsFile in resourceFiles) {
            // Pages render from the inside-out, which is required for us to expose
            // our resources declared in children to the parent where they are emitted
            // however, it also means our resources naturally collect here in an order
            // that is probably not what the site author intends.
            // We reverse the order with insert
            JsResourceLinks.Insert(0, jsBundle.MvcRender(ASSET_PATH + ViewIdentifier + "_#.js"));

    /// <summary>
    /// Bundles CSS files to be emitted with the ResourceLinks directive
    /// </summary>
    /// <param name="resourceFiles">Zero or more project paths to CSS files</param>
    public void AddCssResources(params string[] resourceFiles) {
        // Create a separate reference for each CSS path, since CSS files typically include path-relative images.
        foreach (
            var cssFolder in resourceFiles.
                GroupBy(r => r.Substring(0, r.LastIndexOf('/')).ToLowerInvariant()).
                // Note the CssResourceLinks.Insert command below reverses not only desirably
                // the order of view emission, but also undesirably reverses the order of resources within this one view.
                // for this page we'll 'pre-reverse' them. There's probably a clearer way to address this.
                Reverse()) {
            CSSBundle cssBundle = Bundle.Css();
            foreach (string cssFile in cssFolder) {
            // See JsResourceLinks.Insert comment above
            CssResourceLinks.Insert(0, cssBundle.MvcRender(cssFolder.Key + "/" + ViewIdentifier + "_#.css"));

    #region private implementation
    private string _viewIdentifier = null;
    // ViewIdentifier returns a site-unique name for the current control, such as "shared_signinpartial"
    // Some security wonks may take issue with exposing folder structure here
    // It may be appropriate to obfuscate it with a checksum
    private string ViewIdentifier {
        get {
            if (_viewIdentifier == null) {
                _viewIdentifier =
                    // VirtualPath uniquely identifies the currently rendering View or Partial,
                    // such as "~/Views/Shared/SignInPartial.cshtml"
                    // This "Substring" truncates the ~/Views/ or ~/Areas/ in my build, in others
                    // but it is probably inappropriate to make this assumption.
                    // It is certainly possible to have views in the root.
                    // Substring(8).
                    // It's assumed all of these bundles will be output to a single folder,
                    // to keep filesystem write-access minimal, so we flatten them here.
                    Replace("/", "_").
                    // The following assumes a typical MS filesystem, preserve-but-ignore case.
                    // The .NET string recommendations suggest instead using ToUpperInvariant
                    // for such an operation, but this was just a personal preference.
                    // My IIS rules typically drop the case on all content served.
                    // It may be altogether inappropriate to alter,
                    // although appending the MD5 hash ensure it does no harm on other platforms,
                    // while still collapsing the cases where multiply-cased aliases are used
            return _viewIdentifier;

    private List<MvcHtmlString> CssResourceLinks {
        get { return getContextHtmlStringList("SquishItCssResourceLinks"); }

    private List<MvcHtmlString> JsResourceLinks {
        get { return getContextHtmlStringList("SquishItJsResourceLinks"); }

    // Note that at the resource render, if no bundles of a specific type (.css or .js)
    // have been provided, this performs the unnecessary operation of instanciating a new List<MvcHtmlString>
    // and adding it to the HttpContext.Items. This get/set could benefit from some clarification.
    private List<MvcHtmlString> getContextHtmlStringList(string itemName) {
        IDictionary contextItems = Context.ApplicationInstance.Context.Items;
        List<MvcHtmlString> resourceLinks;
        if (contextItems.Contains(itemName)) {
            resourceLinks = contextItems[itemName] as List<MvcHtmlString>;
        else {
            resourceLinks = new List<MvcHtmlString>();
            contextItems.Add(itemName, resourceLinks);
        return resourceLinks;

    private string[] FilterFileExtension(string[] filenames, string mustEndWith) {
        IEnumerable<string> filtered =
            filenames.Where(r => r.EndsWith(mustEndWith, StringComparison.OrdinalIgnoreCase));
        return filtered.ToArray();
    #endregion private implementation

PageWithHeaderLayout.cshtml (przykład użycia)


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "">
<html xmlns="">
    <meta name="description" content="@ViewBag.Description" />
    <meta name="keywords" content="@ViewBag.Keywords" />
    <link rel="shortcut icon" href="@Url.Content("~/favicon.ico")" type="image/x-icon" />
    <!-- all bundles from all page components are emitted here -->
    <div id="pageContent">

Niestety napisałem to, więc ma wiele ograniczeń. Skrypty nie de-duplikują, wymaga prostego podejścia do wyznaczania pakietów, ostatnio dodałem brzydki hack, aby zezwolić.less wsparcie itp.

Czy istnieją jakieś istniejące rozwiązania, aby to zrobić?