ASP.NET MVC 4.0 application - Visual Studio 2012
I cannot get bundling and minification to work in release mode.
My basic non-understanding is:
Do I have to provide the *.MIN.css, *.MIN.js files beforehand, or should VS minify the files on its own? (ie: I provide a mcimagemanager.js and VS makes a mcimagemanager.MIN.js out of it) ???
Here is a code snippet - which gets called in Global.asax:
public static void RegisterBundles(BundleCollection bundles)
{
var im = new ScriptBundle("~/bundles/MCImageManager").Include(
"~/Scripts/tinymce/plugins/imagemanager/js/mcimagemanager.js"
);
bundles.Add(im);
}
it works fine in Debug - not in Release-mode
Thank you!
No you don't need to provide .min file, nor will the bundler create that version (not something you see in the folder, at least).
The difference is this. Let's say you have both jquery-1.9.1.js and jquery-1.9.1.min.js in your scripts folder.
Debug mode will use jquery-1.9.1.js as the source script, and no it won't be minified or bundled, as the whole bundling/minification is disabled in debug mode (though you can override this).
Release mode will use jquery-1.9.1.min.js AND bundle it with other scripts for that bundle.
If you only have the one file, jquery-1.9.1.js, Release mode will use it and minify and bundle it.
Debug mode will NOT use .min files. So if you use a wildcard to include all files for a scripts directory, your .min files will not be included.
Debug mode, if you look at the rendered HTML, will reference all script files in the bundle individually. In Release mode, there will only be one script reference (with a querystring for versioning) per bundle.
Other relevant reading/posts:
Scripts.Render using outdated javascript file
Force ASP.Net MVC Bundle to render the javascript files in a certain order
Bundling and minification framework do also minification itself. So you just provide plain JavaScript/CSS files. It handle on the one hand files itself in the other hand it handle the registration in the view, for eample: #Scripts.Render("~/bundles/jquery"). So it "know" what file needs to be included in the view, weather it is "normal" or "min" version.
More on that topic you can find in this nice Exercise: http://msdn.microsoft.com/en-us/vs11trainingcourse_aspnetandvisualstudio_topic5.aspx
However this is might even better resource for the MVC oriented application: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification
Related
I'm looking for some best practices and instructions on how to set up and integrate angular-cli (webpack) with Visual Studio 2015 in MVC 5 (not Core).
I realize there's a similar question asked here (how to set up asp.net angular 2 project using Angular-Cli with ASP.NET Core in Visual Studio 2015?), but that was for Asp.net Core only. My project could not move over to Core yet due to server technical issues.
Here is my solution:
Keep an empty index.html
Run ng build to generate index.html with scripts and styles links injected
Create a Gulp/Grunt task to extract all the scripts and styles links from index.html to a json file say manifest.json.(Here I use Cheerio and Gulp-asset-manifest )
Write logic to read manifest.json and output to your MVC views with Razor syntax
It works perfectly for my ASP.net MVC 5 project.
However it cannot work with route lazy load until publicPath is supported, because the url for chunk js files is loaded from root folder not dist folder.
For example:
The correct chunk js files should be:
www.example.com/MyApp/dist/[id].chunk.js
But it will load from:
www.example.com/MyApp/[id].chunk.js
I already created a PR to add pulishPath for angular-cli to solve this issue.
https://github.com/angular/angular-cli/pull/3285
Update:
The PR for publicPath has been merged, lazy loading is no long an issue.
My approach to integrating Angular/CLI and MVC:
The idea here is to separate the front end development experience from the MVC part, so you don’t have to deal with any of the Visual Studio BS, and enjoy the benefits of the Angular CLI and VS Code.
My app is a hybrid app - most pages and main navigation is classic MVC, but some views are in fact single page applications, where the NG app is embedded into an existing view.
I created a new project in the solution to store the SPAs. (You can exclude this project from the solution build)
In the new project, I created a directory for each SPA. Each one of these folders is a standard CLI project (created using ng new)
Development of the ng stuff is done with VS Code, serving the app using ng serve. The development experience with VS Code is awesome!
When we want to embed the app into the MVC view, we first build it for prod using ng build --prod. This creates the bundles in the dist folder.
In the MVC app, I prepared a folder under Scripts\Frontend for each SPA
A gulp task in the MVC project is responsible for copying the bundles from the SPAs' dist folders into the appropriate folders under Scripts\Frontend. Using Task Runner, the task was bound to to Before Build, so it is automatically executed whenever the app is built.
The important gulp command is:
gulp.src('../FrontEndProj/spa1/dist/*bundle.*')
.pipe(gulp.dest('Scripts/Frontend/spa1'));
of course, you need to delete the existing files etc.
In bundles config, I've created a bundle for the styles and a bundle for the scripts. Since prod bundle names are generated with hash by the CLI, I use wildcards:
bundles.add(new script("~/Scripts/spa1/js")
.IncludeDirectory("~/Scripts/Frontend/spa1", inline.*")
.IncludeDirectory("~/Scripts/Frontend/spa1", "polyfills.*")
.IncludeDirectory("~/Scripts/Frontend/spa1", "vendor.*")
.IncludeDirectory("~/Scripts/Frontend/spa1", "main.*"));
(Note that the order you add the files to the bundle is important!)
bundles.add(
new StyleBundle("/Scripts/spa1.css")
.IncludeDirectory("~/Scripts/Frontend/spa1", "*.css")
In your view, add the ng app directive and use the bundles:
#section head {
#Styles.Render("~Scripts/spa1/css")
}
<app-root>Loading...</app-root>
#Scripts.Render("~/Scripts/spa1/js")
I use a slightly different approach to Chang Liu.
After I run ng build -- prod, I copy the files to the root folder,and change the href attribute in index.html to correspond to my IIS virtual directory.
I have a small JS library in my MVC 5 project that I want to be available for external users to load into their apps. At the moment I'm bundling it like so:
bundles.Add(new ScriptBundle("~/clientApi")
.IncludeDirectory("~/Api/clientapps/", "*.js"));
I can then access the bundled library via browser at the path /clientApi.
However, it's always minified, even though I've set my web.config debug=true, and other bundles in my own app are included as non-minified.
How can I make the file/s in the bundle available as a non-minified bundle file?
If you access /clientApi directly then yes it will be the bundled/minified version.
The debug=true option effects your script reference in your own .cshtml file. When debug=true, references to the individual script files are rendered to the client (so the client doesn't use /clientApi at all).
When debug=false, then a reference to /clientApi (with the version query string) is rendered to the client instead, so they get the bundled/minified version... If you give that link to these external users, then that is what is going to get rendered.
That path doesn't care if it is debug or not. It's not like /clientApi is going to bundle but not minify the files depending on your compilation settings... it's just either your app is going to render the bundled/minified path or the individual script paths.
If you want to do debugging/testing in external apps, then they will just have to use the individual script paths.
Even if you do give these external apps the /clientApi reference once testing is done and they are ready to use the bundled/minified version, it doesn't explain how you are going to handle versioning. If you update a script, how will they know to stop caching?
Actually you can serve the bundle unminified if you disable the transforms of the Bundles
protected void Application_Start() {
BundleTable.EnableOptimizations = true; // Force bundling to occur
// If the compilation node in web.config indicates debugging mode is enabled
// then clear all transforms. I.e. disable Js and CSS minification.
if (HttpContext.Current.IsDebuggingEnabled) {
BundleTable.Bundles.ToList().ForEach(b => b.Transforms.Clear());
}
}
RegisterBundles :
bundles.Add(new ScriptBundle("~/bundles/AllScripts").Include(
"~/Scripts/jquery.x123.{version}.js",
"~/Scripts/bootstrap.js",
"~/Scripts/jqRect.js"));
In the Shared Layout file :
#Scripts.Render("~/bundles/AllScripts")
In Global.asax we have :
BundleConfig.RegisterBundles(BundleTable.Bundles);
The scripts didn't combined, also didn't get minified.
This is in release mode.
Is there anything missed?
In your web.config you need to set the attribute debug="false" in the <compilation>-tag.
This means that you can use this flag to allow javascript debugging locally before deploying (debugging minified and bundled javascript is obviously next to impossible).
NOTE: The "Release mode" flag only affects the way the C# (or VB.NET) compiler compiles your classes and is not related to the debug attribute of the <compilation>-tag. Also note that the debug attribute controls whether ASP.NET MVC caches the location of views on disk and thus has a great performance impact: you should always have debug=false in a production environment.
When you develop your project with Debug mode, it doesn't combined and minified. However,
you can force it to do that by setting
BundleTable.EnableOptimizations = true;
I have a MVC 4 application in .NET 4.0. My web hosting provider (network solutions) has virtual directories setup so I can't use the default bundling behavior (I think).
In my _Layout view I have this line:
#Scripts.Render("~/bundles/dd-d2")
In BundleConfig.cs I have
bundles.Add(new ScriptBundle("~/bundles/dd-d2").Include(
"~/Scripts/dd-d2.js"));
And everything works fine when I run in visual studio. But when i upload to my web hosting, The file is not found because it appends the virtual folder in front of the bundle path.
instead of /bundles/dd-d2?v=BlahBlah, I get /ROOT_FOLDER/bundles/dd-d2?v=BlahBlah
I fixed this issue by adding this to my line in the _Layout file
#Scripts.Render(Url.Content("~/bundles/dd-d2"))
The Url.Content helper converts the virtual path into an app absolute path and it finds my bundled script files.
Now, the problem I am having with that approach is, if I make a change to the javascript file, it is NOT reflected in the outputted bundled script file. It always has the old information in it even when I upload it to my web hosting. Is this file cached somewhere? Is there a better way of doing this? I would like to take advantage of the minification of my script files by using bundling.
if I do it this way, it does work
<script src="#Url.Content("~/Scripts/dd-d2.js")" type="text/javascript"></script>
But then my script is not minified.
I have an MVC 4.5 project that has most of the UI logic organized in jQuery plugins. I want to protect my code by minification and bundling (While I understand that minification will only do so much as far as protection, it's better than leaving formatted and documented source files on the server.)
Ideally, I want my dev server to work as is -- files are non-minified and separated. But, when I deploy to the production server, I want the source files to be removed and only minified bundles to be available. Also note, on many occasions my jQuery plugins load other plugins from JavaScript code (I use head.js), so I cannot use #Script.Render for that.
What technologies do I use -- built-in MVC bundling, SquishIt, Bundler or do I need to resort to MSBuild and Microsoft Axaj Minifier? To recap, I want to remove source JS files and just be left with minified bundles in production, and, preferably, find a way to not change head.js references based on whether files are minified or not.
Thanks for your advice.
Just thought I respond with what I ended up doing here:
To recap: I wanted to obfuscate my source files with minification while not exposing the source JS files in production. I also wanted for head.js to resolve source file URLs to bundle URLs:
Put all non-minified javascript files in a folder viewable only to Admin role
Used bundling built-in to ASP.NET MVC 4.5 to generate bundles
Pointed my head.js tag to an MVC controller that returned head.js code + a javascript array with an x-ref between raw URLs and bundle URLs (available from BundleTable static object)
Bundling occurs outside of ASP.NET membership, so bundles are generated and available to anonymous users even though the source files are in the folder only accessible by Admin. Then, the trick of dynamically augmenting head.js code with server-side generated bundle URLs takes care of calling bundles from JS files.