📜 ⬆️ ⬇️

WebMarkupMin: Minimize KnockoutJS and AngularJS views

WebMarkupMin, KnockoutJS and AngularJS logos
Starting from version 0.9.0, WebMarkupMin supports minimization of KnockoutJS (hereafter simply Knockout) and AngularJS (hereafter simply Angular) views. Many of you may ask the question: "Why is Knockout and Angular, not Mustache or Underscore ?" This choice was made for the following reasons:
  1. DOM based templates. The template engines built into Knockout and Angular are based on DOM-based templates (DOM-based templates), and not on string-based templates like Mustache and Underscore. The code of such templates does not contain software inserts (for example, {{…}} or <%…%> ) outside the text content of elements (tags) and attribute values, which allows minimizing it as regular HTML.
  2. Popularity among .NET developers. Knockout was originally created for .NET developers to allow them to transfer their experience in developing MVVM applications from WPF and Silverlight to a regular web. As for Angular, it doesn’t need any introduction at all and its popularity among web developers in general beats all possible records. In addition, the popularity of these libraries among .NET-developers contributed to a huge number of articles of the evangelist Microsoft John Pope .
  3. High compression efficiency for binding expressions. The binding expressions in Knockout and Angular are actually simple JavaScript code or objects in JSON format that can be compressed with the JS minimizer.


New configuration properties


By default, the minimization of views is disabled, and to enable it, you need to change the values ​​of the following properties of the HtmlMinificationSettings class:
PropertyData typeDefault valueDescription
ProcessableScriptTypeListLineEmpty lineContains a comma-separated list of script tag types (for example, text/html,text/ng-template ), the contents of which must be processed by the HTML minimizer.
MinifyKnockoutBindingExpressionsBooleanfalseThe flag responsible for minimizing the Knockout bindings in the data-bind attributes and containerless comments.
MinifyAngularBindingExpressionsBooleanfalseThe flag responsible for minimizing Angular binding expressions in Mustache-like tags ( {{ }} ) and directives.
CustomAngularDirectiveListLineEmpty lineContains a comma-separated list of custom Angular directives (for example, myDir,btfCarousel ) that contain binding expressions. If the value of the MinifyAngularBindingExpressions property is true , then the expressions in the user directives will be minimized.
Consider each property in more detail:

ProcessableScriptTypeList


Prior to version 0.9.0, script tags that do not contain JavaScript code were simply ignored by the HTML minimizer. This was done because these tags can contain anything from VBScript code to Handlebars templates. But at the same time, if the script tag contains the code of the DOM template, then it would be worth letting it through the HTML minimizer. Therefore, users were given the opportunity to determine the list of acceptable content types themselves.

Returning to the topic of the article, I will give a few examples:
  1. If you want to minimize the Knockout views, then the ProcessableScriptTypeList property ProcessableScriptTypeList be set to a value equal to text/html .
  2. If we are dealing with Angular views, then text/ng-template .
  3. If the project uses two libraries at once, then you need to list the content types separated by commas: text/html,text/ng-template .

You also need to understand that we are not limited only to Knockout and Angular, theoretically we have the ability to minimize any DOM-based template (for example, we can specify the text/x-kendo-tmpl content type for Kendo MVVM views, which differ little from Knockout ).
')

MinifyKnockoutBindingExpressions


Usually when I talk about WebMarkupMin, then as an example I cite the code for the shell.html file from the demo project HotTowel :

 <div> <header> <!--ko compose: {view: 'nav'} --><!--/ko--> </header> <section id="content" class="main container-fluid"> <!--ko compose: {model: router.activeItem, afterCompose: router.afterCompose, transition: 'entrance'} --> <!--/ko--> </section> <footer> <!--ko compose: {view: 'footer'} --><!--/ko--> </footer> </div> 

After minimization, this code takes the following form:

 <div><header><!--ko compose: {view: 'nav'}--><!--/ko--></header><section id="content" class="main container-fluid"><!--ko compose: {model: router.activeItem, afterCompose: router.afterCompose, transition: 'entrance'}--> <!--/ko--></section><footer><!--ko compose: {view: 'footer'}--><!--/ko--></footer></div> 

It immediately catches the eye that the binding expressions (hereafter just expressions) in containerless comments (the so-called containerless control flow syntax ) contain too many whitespace characters and would be worth minimizing them.

Take for example the expression compose: {view: 'nav'} , which, in its essence, is an object in JSON format without external curly braces. We can wrap it in braces: {compose: {view: 'nav'}} , and process it with a JS minimizer. Then remove the external curly brackets from the minimized code and return it to containerless comment.

If the MinifyKnockoutBindingExpressions property MinifyKnockoutBindingExpressions set to true , then all the actions described above will be applied to the expressions. For these purposes, CrockfordJsMinifier is always used as a JS minimizer, because only it can correctly minimize JSON code ( MsAjaxJsMinifier and YuiJsMinifier not suitable for this purpose).

With the new settings we get the following code:

 <div><header><!--ko compose:{view:'nav'}--><!--/ko--></header><section id="content" class="main container-fluid"><!--ko compose:{model:router.activeItem,afterCompose:router.afterCompose,transition:'entrance'}--> <!--/ko--></section><footer><!--ko compose:{view:'footer'}--><!--/ko--></footer></div> 

The same actions are performed with data-bind attributes. For example, we have the following code:

 <span data-bind="text: price() > 50 ? 'expensive' : 'cheap'"></span> 

After minimization, it will look like this:

 <span data-bind="text:price()>50?'expensive':'cheap'"></span> 

MinifyAngularBindingExpressions


According to the documentation , Angular expressions are JavaScript-like code fragments. However, they can be minimized with CrockfordJsMinifier . The only problem was only one-time binding expressions (starting with :: . The original JSMin always removed the whitespace before it, but after making small changes to the CrockfordJsMinifier code, the problem was fixed.

In Angular, expressions can be contained in Mustache-like tags ( {{ }} ) and directives.

In turn, Mustache-like tags may be in the text content of the elements:

 <strong>Price:</strong> {{ 3 * 10 | currency }} 

and in attribute values:

 <img src="/Content/images/icons/{{ iconName + '.png' }}"> 

Accordingly, if the MinifyAngularBindingExpressions property MinifyAngularBindingExpressions set to true , then we will get the following results:

 <strong>Price:</strong> {{3*10|currency}} 
and
 <img src="/Content/images/icons/{{iconName+'.png'}}"> 

With directives, the situation is much more complicated. In the template code, directives can be represented in 4 ways:
  1. In the form of elements
  2. In the form of attributes
  3. As the contents of class attributes
  4. In the form of comments

The names of the directives may also have different spellings. For example, for the ngBind directive there are the following options:

All these features are taken into account when minimizing.

In addition, not all directives contain expressions. Some directives may also contain: patterns, simple values, or nothing at all. In order to separate directives that contain expressions from other directives, the following list is used: ngController , ngCopy , ngCut , ngDblclick , ngDisabled , ngFocus , ngHide , ngIf ngInit , ngKeydown , ngKeypress , ngKeyup , ngModelOptions , ngMousedown , ngMouseenter , ngMouseleave , ngMousemove

Items


The HTML minimizer processes only one directive element - ngPluralize . More specifically, expressions in the count and when attributes are minimized.

Suppose we have the following code:

 <ng-pluralize count=" personCount " when="{ '0': 'Nobody is viewing.', 'one': '1 person is viewing.', 'other': '{} people are viewing.' }"> </ng-pluralize> 

After minimization, it will look like this:

 <ng-pluralize count="personCount" when="{'0':'Nobody is viewing.','one':'1 person is viewing.','other':'{} people are viewing.'}"> </ng-pluralize> 

Attributes


Minimizing expressions in attribute directives in Angular is even easier than minimizing the contents of the data-bind attributes in Knockout, because here you don’t need to think about external curly braces.

Consider minimizing expressions in attribute directives ngRepeat directive as an ngRepeat :

 <li ng-repeat="customer in customers | filter:nameText | orderBy:'name'"> {{customer.name}} - {{customer.city}} </li> 

Despite the fact that the attribute contains “incorrect” code (from the JavaScript point of view), the minimization is still successful:

 <li ng-repeat="customer in customers|filter:nameText|orderBy:'name'">{{customer.name}} - {{customer.city}}</li> 

Classes


The main difference between class directives and attribute directives is that the class attribute can contain several directives at once:

 <span class="ng-bind: name; ng-cloak; ng-style: { 'background-color': 'lime' };"></span> 

This problem was solved:

 <span class="ng-bind:name;ng-cloak;ng-style:{'background-color':'lime'}"></span> 

Comments


Unfortunately, in Angular there is not a single built-in directive that could be represented as a comment. Therefore, we will consider a slightly contrived example, where the user directive myDir will be used as a comment directive:

 <!-- directive: my-dir 1 + 1 --> 

After minimization, we get the following code:

 <!--directive:my-dir 1+1--> 

CustomAngularDirectiveList


Since Angular has the ability to create its own directives, users have been given the opportunity to add the names of these directives to a special list. Custom directives specified in the CustomAngularDirectiveList property will be processed by the minimizer as well as built-in Angular directives.

Web Essentials 2013 Support


If you store your views in separate HTML files, then the standard ASP.NET extensions that come with WebMarkupMin will be of little use. In this case, you need a fundamentally different tool that will minimize the HTML files at the project build stage. At the moment there is only one such tool - this is the VS-extension Web Essentials 2013 .

The latest stable version of Web Essentials 2013 (version 2.3) supports the old version of WebMarkupMin (version 0.8.21). Therefore, you will need to install the nightly assembly of Web Essentials 2013 (there is an installation guide on the expansion site).

The last, at the time of writing, the nightly build (version 2.3.4.1) supports WebMarkupMin 0.9.3 and allows you to configure all the considered configuration properties:

HTML minimization settings in Web Essentials 2013 (version 2.3.4.1)

Links


  1. WebMarkupMin project page on CodePlex
  2. Article "WebMarkupMin HTML Minifier - modern HTML minimizer for the .NET platform"
  3. The official site of the VS-extensions Web Essentials
  4. Article "HTML minimization in Web Essentials 2013"
  5. Article "HTML minimization in Web Essentials 2013: What has changed over the year?"

UPD1: October 17 released a stable version of Web Essentials 2013 (version 2.4), which supports all the presented functionality.

Source: https://habr.com/ru/post/239025/


All Articles