I have the following bundle configured in BundleConfig.cs:
bundles.Add(new StyleBundle("~/bundles/css").Include(
"~/assets/bootstrap/css/bootstrap.css",
"~/assets/css/global/all.css"));
and I reference it using the following:
#Styles.Render("~/bundles/css")
When I'm in debug mode (web.config compilation debug="true") it works as expected in that it renders both css files as normal ie:
<link href="/assets/bootstrap/css/bootstrap.css" rel="stylesheet"/>
<link href="/assets/css/global/all.css" rel="stylesheet"/>
However when I set debug="false" the above behaviour still occurs in that it does recognise the files, however it's just rendering them as normal.
To confirm bundling can definitely work I've enabled optimizations in BundleConfig ie BundleTable.EnableOptimizations = true;
Whenever I do the above, it bundles the css and appears as expected ie:
<link href="/bundles/css?v=WBKHkZAJly7jUzHrVDT8SwfaQE-CA9dbOUQUlLKadNE1" rel="stylesheet"/>
EDIT:
A few people have mentioned that adding the following code to my BundleConfig.cs file will achieve what I am after:
#if DEBUG
BundleTable.EnableOptimizations = false;
#else
BundleTable.EnableOptimizations = true;
#endif
I understand and appreciate this response, however according to the documentation, the default behaviour of MVC bundling is to bundle in release mode but not in debug mode. I don't see why I should need to add extra code to make it do this when it should be doing it already.
EDIT 2
I've a confession to make. It turns out I had the web.config from the Views folder opened and not the main web.config. I changed the setting in the main web.config and this works just fine for me. I blame ReSharper
This is the default behavior.
Bundling and minification is enabled or disabled by setting the value of the debug attribute in the compilation Element in the Web.config file.
<system.web>
<compilation debug="true" />
<!-- Lines removed for clarity. -->
</system.web>
To enable bundling and minification, set the debug value to "false". You can override the Web.config setting with the EnableOptimizations property on the BundleTable class. The following code enables bundling and minification and overrides any setting in the Web.config file.
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
BundleTable.EnableOptimizations = true;
}
http://www.asp.net/mvc/overview/performance/bundling-and-minification
The way that I get around this is to force it in the BundleConfig to do exactly what I want it to do. I don't think MVC4 had the same options with the config file (or I just never got them to work).
So this is what I have at the end of my RegisterBundles method:
#if DEBUG
BundleTable.EnableOptimizations = false;
#else
BundleTable.EnableOptimizations = true;
#endif
This way it's always there, plain to see. However, you do have to remember to put that in there when you're starting up the project, but that's not a huge deal.
If you're not familiar with these, the #if DEBUG is a preprocessor directives that tells the CLR to do what is in that block when the DEBUG build parameter is present (should only be present in DEBUG mode, though that can be changed from the Project Properties). If that variable is not present (Release mode, or any other mode), then it will do the other block.
The default Release Web.config transform removes the debug attribute like so:
<compilation xdt:Transform="RemoveAttributes(debug)" />
However, this will not cause the expected bundling behavior to occur. Instead, you must create a transform that literally sets the debug attribute to "false", like so:
<compilation debug="false" xdt:Transform="SetAttributes" />
Another possible issue is that inside of your Global.asax.cs file in the Application_Start() method, you're missing the call to your BundleConfig.
For example, assuming your method is using the default name RegisterBundles(BundleCollection bundles) then inside of your Global.asax.cs file, you'll want to add BundleConfig.RegisterBundles(BundleTable.Bundles); in the Application_Start() method.
So your Global.asax.cs file might look something like this:
using System;
using System.Collection.Generic;
using System.Linq;
using System.Web;
using System.Http;
using System.Mvc;
using System.Routing;
using System.Optimization;
namespace MyProject
{
public class MvcApplication : System.Web.HttpApplication
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// ** This line right below might be what you are missing **
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
I had this problem when I changed the css folder. My problem was that I changed the BundleConfig file for css files:
BundleConfig.cs:
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/css/bootstrap.css",
....
Views included:
#Styles.Render("~/Content/css")
For some reason, this didn't worked. 'bundles' seems to be a keyword or something (not sure). You must left it this way:
bundles.Add(new StyleBundle("~/bundles/css").Include(
"~/Content/css/bootstrap.css", ...
Views:
...
#Styles.Render("~/bundles/css")
...
I have no need to modify 'BundleTable.EnableOptimizations' nor web.config nor anything else.
Hope this helps someone.
After fighting against this issue for several hours, I recommend that you try this as well:
Add this at the very beginning of the view you are using your bundle:
#{
BundleTable.EnableOptimizations = true;
}
Reset IIS
Reload your page and check if minify works
In my case somewhere in my solution was changing "BundleTable.EnableOptimizations" from true to false. When I force that to TRUE right before the bundle is used, I got this working.
After I noticed it works, I moved that into a static method at my BundleConfig class:
public static void EnableOptimizations()
{
#if DEBUG
BundleTable.EnableOptimizations = false;
#else
BundleTable.EnableOptimizations = true;
#endif
}
So I can call it from the view and have it minify disabled for developers
#{
BundleConfig.EnableOptimizations();
}
Make sure that you do not have any minified file in you BundleConfig class,
e.g bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include("~/Scripts/js/popper.min.js"));
Instead use bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include("~/Scripts/js/popper.js"));
In addition to all other replies, if you still cannot bundle your CSS and JS files, be sure that you are addressing the correct paths and/or folder/file names.
In my case, some of the files I'm addressing are actually missing, and some has typo while addressing. After you correct these kind of things bundle should work properly.
Lastly, if you have references via relative paths on your CSS or JS files (e.g. url(../../css/img/loading.gif)), you might want to turn them into absolute paths (e.g. url(/Content/css/img/loading.gif)) otherwise they won't be worked or rendered eventhough files are successfully bundled. Relative paths in bundle show differences from browsers' point of view.
<compilation debug="true" targetFramework="4.5.2" />
solve my problem.
debug=true was missing in my web config while publishing.
Related
I am using RegisterBundles to bundle my CSS files and server them from a virtual folder.
I have the following code in RegisterBundles function:
bundles.Add(new StyleBundle("~/Content/all/")
.Include("~/foundation/css/normalize.css",new CssRewriteUrlTransform())
.Include("~/foundation/css/foundation.min.css", new CssRewriteUrlTransform())
.Include("~/foundation/icons/foundation-icons.css", new CssRewriteUrlTransform())
.Include("~/Content/jquery.mCustomScrollbar.css", new CssRewriteUrlTransform())
);
In my page I have the following code:
<%= System.Web.Optimization.Styles.Render("~/Content/all/") %>
The problem is that I get a list of all the css files instead of one combined and minified virtual path to the css. I need CssRewriteUrlTransform for all the css files to make sure the relative paths are maintained correctly because they exist in different folders.
In order to allow Bundled and Minified to work, you should do one of following:
Change compilation to debug="false" in web.config
<system.web>
<compilation debug="false" />
</system.web>
In the same file of RegisterBundles, add following line
BundleTable.EnableOptimizations = true;
I use MVC helpers like this: #Styles.Render("~/Content/css") to add styles on the page, but I don't like this relative path everywhere. And it's very hard to fix all occcurentcies, if I change folder structure.
Could I configure somewhere to setup that #Styles should map to '~/Content' and #Scripts to ~/Scripts to be able instead of #Styles.Render("~/Content/css") write just
#Styles.Render("main.css") for example?
First of all, that is not a helper but a bundle. Bundles are defined inside a virtual path in ISS when you disable debug.
For example:
bundles.Add(new StyleBundle("~/Content/css")
.Include("~/My/Css/File.css"));
Will configure a virtual path in /Content/css in IIS.
You can change the virtual path to whatever you want.
I created a controller path to return a css file, which works, and returns Response.ContentType = "text/css".
Now I'm trying to put that URL in my bundles file, like this:
bundles.Add(new StyleBundle("~/Content/custom").Include(
"~/CSS/Custom/1"
));
NOTE: /CSS/Custom/1 is a route that returns a text/css file.
In my view I have:
#Styles.Render("~/Content/custom")
When I build the project, the bundler returns this in my HTML:
<link href="/Content/custom?v=" rel="stylesheet"/>
When I view the files source, It's empty.
How do I get this to work?
Not sure why you'd want to do this
After taking a look at it in reflector, during the bundling process it will take the virtual path to the item and check to make sure the file exists. (Code below) So bundling without CDN absolutely requires a file, not an application route.
if ((this.VirtualPathProvider == null) || this.VirtualPathProvider.FileExists(virtualPath))
{
base.Add(new BundleItem(virtualPath, transforms));
}
CDNs never hit this line, they take a different path.
bundles.UseCdn = true;
bundles.Add(new StyleBundle("~/Content/custom", "/CSS/Custom/1"));
BundleTable.EnableOptimizations = true;
Out of the box, you can only bundle physical files. If you need the CSS to be dynamically generated for some reason, you might consider creating a custom bundle. But if it were me, I'd just leave this as a separate download.
I read the article about bundling and monification, specially about using CDN, but there are some things unclear to me.
Having the example :
public static void RegisterBundles(BundleCollection bundles)
{
//bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
// "~/Scripts/jquery-{version}.js"));
bundles.UseCdn = true; //enable CDN support
//add link to jquery on the CDN
var jqueryCdnPath =
"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";
bundles.Add(new ScriptBundle("~/bundles/jquery",
jqueryCdnPath).Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
}
Is there a possibility to use the {version} format of CDN references, like for the "local" ones?
What is the point of including in the bundles the already minified version of the script, like jquery-1.7.1.min.js? What if it does not exist? Should it not search if the .min file exist and/or generate it respectively?
using System.Web;
using System.Web.Optimization;
namespace MvcApp
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js").Include("~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap","https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js").Include("~/Scripts/bootstrap.js"));
bundles.Add(new StyleBundle("~/Content/css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css").Include("~/Content/bootstrap.css"));
BundleTable.EnableOptimizations = true;
bundles.UseCdn = true;
}
}
}
What a lot of developers don't realized is that there is an overload for class constructor of ScriptBundle and StyleBundle, which takes two string parameters, for example for the ScriptBundle it would be ScriptBundle(string, string) and for the StyleBundle it would be StyleBundle(string, string). The first parameter is the virtual path and the second parameter is the cdnPath.
We might be asking yourself, if it takes two parameters, how does MVC know which one to use? Well, the cdn location is used only when the BundleTable.EnableOptimizations property is set to true.
Setting the EnableOptimization property to true tells MVC to use the use the minified version of the file instead of the regular version.
When this property is set to true, and the cdn path is present MVC will use the cdn path instead of the local virtual path.
There is one more property you have to set to true and that is the bundles.UseCdn.
This tells MVC to use the cdn location instead of the local version. If the BundleTable.EnableOptimization is set to false, then the local version is used automatically as a fall back because the cdn version is the minified version.
Read this blog its clear about your think:
http://www.techjunkieblog.com/2015/06/aspnet-mvc-5-configure-bundleconfig.html
You can't to my knowledge. But you can keep a table of cdns and populate when the bundles are loaded. When a new version comes out you wish to use, add/replace entry in the db.
//get from db
List<string> cdns = new List<string>();
foreach (string cdn in cdns)
{
bundles.Add(new ScriptBundle("~/bundles/jquery",cdn).Include("~/Scripts/jquery-{version}.js"));
}
I agree on the min part. For the doesn't exist part of the question, scroll down and read about "Using a CDN". There's an example to show how to check. You essentially need to have a local copy as backup of you can reference another cdn I suppose.
Is there a possibility to use the {version} format of CDN references,
like for the "local" ones?
The {version} placeholder is primarily for saving time typing the explicit number so that the build could look for files on local disk. Since the same search could not be done on a remote server, you will need to specify an exact URL explicitly.
What is the point of including in the bundles the already minified
version of the script, like jquery-1.7.1.min.js? What if it does not
exist?
The key benefit to go with this bundling syntax is to conditionally switch between different URLs for script and style tags in the final HTML.
When a requested file does not exist, the bundling process will skip it.
Should it not search if the .min file exist and/or generate it
respectively?
Yes, it applies minification before bundling as you can see:
In MVC4's Web.Optimization bundling / minimization, is it possible to register a bundle on one site (our static cookieless domain) and then use that bundle on another site (our webapp domain)?
eg static.myapp.com has a BundleConfig.cs with
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/Scripts/static")
.Include("~/Scripts/*.js"));
}
Can that bundle be used in a view on the webapp's domain, eg www.myapp.com has this in Site.Master
<%= Scripts.Render("static.myapp.com/Scripts/static") %>
Can this be done with MVC4 bundling? Serving static files from a cookieless static domain is a well known performance improvement.
Bundling in ASP.net MVC allows you to optimize the deployment of scripts and stylesheets by replacing the placeholder Scripts.Render() at Runtime rather than at Design time. When the page is parsed and pushed to the client, the bundles registered to the calling server are parsed into the output buffer. Therefore the app serving the content must be running the bundler service. If a web app not running bundling encountered the Scripts.Render() element, it would either output null or throw an exception.
you can, however, use CDN references in your RegisterBundles method, like:
bundles.UseCdn = true; //enable CDN support
//add link to jquery on the CDN
var jqueryCdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";
bundles.Add(new ScriptBundle("~/bundles/jquery",
jqueryCdnPath).Include(
"~/Scripts/jquery-{version}.js"));
In the code above, jQuery will be requested from the CDN while in release mode and the debug version of jQuery will be fetched locally in debug mode. When using a CDN, you should have a fallback mechanism in case the CDN request fails.
Edit
You could use ASP.Net MVC to serve as a CDN from static.myapp.com something like
routes.MapRoute(
"CDN",
"cdn",
new { controller = "Webpage", action = "Fetch" }
);
[OutputCache(Duration=300, Location=OutputCacheLocation.Any)]
public ActionResult Fetch()
{
return new FileStreamResult(
GetScriptBundle(Request.QueryString["url"]),
"text/javascript");
}
So I just ran into this requirement and I solved it neatly in the page effectively similar to this;
<%
string jsStore = Context.IsDebuggingEnabled ? "~" : "static.mydomain.com";
string scriptTagFormat = Scripts.DefaultTagFormat.Replace("{0}", Url.Content(jsStore).TrimEnd('/') + "{0}");
%>
and
<%=Scripts.RenderFormat(scriptTagFormat, "~/bundles/allMyJS")%>
Of course you could also use DefaultTagFormat in RegisterBundles() but this offered more flexibility because;
You can read jsStore from alternate web.configs for different environments.
You can modify jsStore at runtime to match different {SERVER_NAME} hosts if you have dev, test sites etc.
You can also use it with static tags like this;
<script src='#Url.Content(jsStore + "/scripts/etc/lt-ie10.min.js")'></script>
The same approach can be used for the CSS.
Yes, see my answer here: How to make bundles unminify and list individual files when using a cookieless static content server?
Not only can you reference a different domain, but if you use a custom VirtualPathProvider like the one I provided, you can also keep the ability to list the files individually while in DEBUG mode.
Besides being easier to debug in the browser, you also won't have to rebuild to update the bundles every time you make a JS or CSS change while developing (just when adding or removing files).