Make ASP.NET bundling specify media=screen for CSS bundle

后端 未结 8 1233
情深已故
情深已故 2020-12-23 13:47

I\'m just trying out ASP.NET 4.5 bundling and minification, and ran into an issue.

I\'ve got around 10 css files, of which 2 were originally referenced in the layout

相关标签:
8条回答
  • 2020-12-23 14:18

    Another option to solve this issue, without compromising the debug ability, could be:

    public static IHtmlString Render(string path, IDictionary<string, object> htmlAttributes)
    {
        var attributes = BuildHtmlStringFrom(htmlAttributes);
    
    #if DEBUG
        var originalHtml = Styles.Render(path).ToHtmlString();
        string tagsWithAttributes = originalHtml.Replace("/>", attributes + "/>");
        return MvcHtmlString.Create(tagsWithAttributes);
    #endif
    
        string tagWithAttribute = string.Format(
            "<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\"{1} />", 
            Styles.Url(path), attributes);
    
        return MvcHtmlString.Create(tagWithAttribute);
    }
    

    What I'm doing is just appending the given html attributes to the end of the tags (on debug mode) or to the end of the only link tag (when minification/bundling are enabled).

    The usage in views:

    @Bundles.Render("~/css/print", new { media = "print" })
    

    The rest of the code:

    public static IHtmlString Render(string path, object htmlAttributes)
    {
        return Render(path, new RouteValueDictionary(htmlAttributes));
    }
    
    private static string BuildHtmlStringFrom(IEnumerable<KeyValuePair<string, object>> htmlAttributes)
    {
        var builder = new StringBuilder();
    
        foreach (var attribute in htmlAttributes)
        {
            builder.AppendFormat(" {0}=\"{1}\"", attribute.Key, attribute.Value);
        }
    
        return builder.ToString();
    }
    

    I've wrote a blog post about this subject: http://danielcorreia.net/blog/quick-start-to-mvc4-bundling/

    0 讨论(0)
  • 2020-12-23 14:19

    Unfortunately there isn't a great way to hook into how the tags are rendered currently, we thought about adding a hook so you could add your own method to render each script/style tag. It sounds like we do need to do that. Should be pretty simple to add, I'll create a work item to enable this scenario...

    As a temporary workaround, if you are willing to lose the debug/release functionality that Styles.Render gives you, you can render a reference to the bundle using Styles.Url which would give you just the bundle url, you can embed that inside your own tag.

    0 讨论(0)
  • 2020-12-23 14:21

    Web Forms Solution

    In BundleConfig.cs:

    //Print css must be a separate bundle since we are going to render it with a media=print
    Bundles.Add(new StyleBundle("~/bundles/printCSS").Include("~/Content/Print.css"));
    

    Master Page:

    <asp:Literal runat="server" ID="litCssPrint" />
    

    Master Page Code File:

    litCssPrint.Text = Styles.RenderFormat(@"<link href=""{0}"" rel=""stylesheet"" type=""text/css"" media=""print"" />", "~/bundles/printCSS").ToHtmlString();
    
    0 讨论(0)
  • 2020-12-23 14:22

    So complicated, why not to use:

    bundles.Add<StylesheetBundle>("~/Css/site.css", b => b.Media = "screen");
    

    ?

    0 讨论(0)
  • 2020-12-23 14:29

    Why not just use @media print? Check out http://www.phpied.com/5-years-later-print-css-still-sucks/

    0 讨论(0)
  • 2020-12-23 14:30

    I took Adam Tal's suggestion a little further.

    I'm probably over-coding it, but for readability, I created a static class to kind of mimic the Styles.Render format.

    public static class StyleExtensions
    {
        public enum Format
        {
            Async,
            Preload,
        }
    
        public static IHtmlString Render(string contentPath, Format format)
        {
            switch (format)
            {
                case Format.Async:
                return contentPath.ToAsyncFormat();
                case Format.Preload:
                return contentPath.ToPreloadFormat();
                default:
                return new HtmlString(string.Empty);
            }
        }
    
        public static IHtmlString RenderAsync(string contentPath)
        {
            return contentPath.ToAsyncFormat();
        }
    
        public static IHtmlString RenderPreload(string contentPath)
        {
            return contentPath.ToPreloadFormat();
        }
    
        public static IHtmlString ToAsyncFormat(this string contentPath)
        {
            return Styles.RenderFormat("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" media=\"print\" onload=\"this.media='all';this.onload=null;\">", contentPath);
        }
    
        public static IHtmlString ToPreloadFormat(this string contentPath)
        {
            return Styles.RenderFormat("<link rel=\"preload\" href=\"{0}\" as=\"style\" onload=\"this.rel='stylesheet';this.onload=null;\">", contentPath);
        }
    }
    

    I would probably erase either the direct constructor or the enum constructor, and you could really put the string extension inside the method too, depending on whatever makes more sense to you, but you'd call it either of these ways accordingly:

    @StyleExtensions.Render("~/Content/bundle-bootstrap", StyleExtensions.Format.Preload)
    @StyleExtensions.Render("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap", StyleExtensions.Format.Preload)
    @StyleExtensions.Render(Url.Content("~/Content/Styles/Primary.min.css"), StyleExtensions.Format.Async)
    

    or

    @StyleExtensions.RenderPreload("~/Content/bundle-bootstrap")
    @StyleExtensions.RenderPreload("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap")
    @StyleExtensions.RenderAsync(Url.Content("~/Content/Styles/Primary.min.css"))
    
    0 讨论(0)
提交回复
热议问题