微软JavaScript和CSS压缩器AjaxMinifier
"
为了提高JavaScript和CSS在网络上的传输性能,在项目部署前,通常需要压缩它们,其实针对JS和CSS的压缩工具已经很多,我通常使用谷歌的ClosureCompiler、雅虎的YUICompress和JsMin工具,要比压缩率的话,YUICompress可以说是中规中矩,ClosureCompiler则非常的彻底,是我见过压缩率最高的压缩器,但压缩率高并不意味着就好,当压缩率越高时,风险也就越大,甚至改变原本语句的语义。YUICompress和ClosureCompiler都采用java开发,但后来有人将YUICompress翻译了个C#版本(YUI-Compressor-For-NET),并提C#压缩接口,支持在VS生成项目时自动压缩,C#版本的YUICompress源码已托管在了GIHUB上,您可以在搜索引擎找到它。
微软自家也有一个名为MicrosoftAjaxMinifier的压缩利器,它采用C#开发,开放源码,与VisualStudio高度集成(可配置生成项目时自动压缩),您可以通过本文末尾的链接访问官方网站下载工具,为了深入学习这款压缩工具和压缩原理,零度博主翻译了官方的帮助指南文档,在此分享,希望对您有所帮助。
1、JavaScript压缩器介绍
JavaScript不是编译性语言,用户通过浏览器客户端下载文本代码块并解释执行,详细的容易维护的编码风格虽然有助于促进软件工程的规范化管理,它的性能却是软件开发工程师们很头痛的问题,因为具有格式的代码虽然易读,比如:空白,缩进、注释和一些便于阅读的语句,但这些额外的字符对浏览器来说无任何用途,它却大大增加了传输到客户端的体积,压缩JavaScript和CSS有助于提高性能。
第一个级别是删除空白和注释,而无需改变代码的语义。比如JsMin就是这么一款工具。
第二个级别是删除多余的分号和花括号,在JavaScript中分号不是语句的结束符,有的语句并不需要一个花括号,这一切都是为了可读性,在部署之前我们应该删除它,但删除不慎,可能会改变代码的语义。
第三个级别是缩短局部变量的名称,规范的变量名,有助于提高可读性,在部署之前,可压缩变量名,但你必须非常仔细地进行选择,不能压缩全局变量和函数名,因为其它文件可能会引用这些全局变量和函数名。
第四个级别是删除未被使用的变量,但您必须得确保函数或变量真的不被使用。
第五个级别是反复使用全局变量和对象属性时,可用1个字母去代替较长的全局变量名和对象属性名,其它多处可直接引用1个字母短变量名。
使用Microsoft Ajax Minifier压缩代码,默认情况下将:去除注释、 空白、不必要的分号和括号 、将局部变量和函数重命名为较短的名称、删除未使用的或不必要的代码,但您可以通过开关〖-rename〗和〖-unused〗选项改变默认的压缩行为。
2、命令行使用方法
微软ajaxmin.exe是一个命令行程序,不提供窗口界面,一切操作都通过命令来完成压缩,您可以点击本文末尾的零度下载按钮下载安装, 安装后方可在cmd命令行中使用,不带任何参数直接输入〖ajaxmin〗或者〖ajaxmin –?〗将显示所有可用命令的帮助说明,要压缩代码,你通常使用如下的参数:
ajaxmin inputfile.js
上面的命令只指定输入文件,未指定输出文件,将默认在控制台打印压缩后的结果,如果您想将压缩结果保存到一个文件,可以使用〖–OUT〗选项。
ajaxmin inputfile.js –out outputfile.js
默认情况下,如果输出文件已经存在,这将引发一个错误,若要覆盖已有的文件,可使用〖–CLOBBER〗选项。
ajaxmin inputfile.js –out outputfile.js –clobber
默认情况下,该工具将重命名并缩短所有局部变量名和局部函数名,如果您不想重缩短任何局部变量名或函数,请使用〖–RENAME〗选项,并赋none值。
ajaxmin –rename:none inputfile.js –out outputfile.js
如果您要指定某些变量或者函数具有本地化特性,您可以用字母L开头来命名它们,例如弹出文本框显示消息:
var L_hello_text = ""Hello there! ""; alert(L_hello_text);
通过这样本地化后的变量,可指定localization选项,这将导致L开头的变量和函数名不会被重命名和缩短。
ajaxmin –rename:localization inputfile.js –out outputfile.js
若要在检测编写的代码和潜在的BUG,请使用〖–ANALYZE〗选项,可用〖–GLOBAL〗选项指定要分析的全局变量,这在分析指定的变量是非常的有用。
ajaxmin –analyze –global:Msn,jQuery inputfile.js –out outputfile.js
警告级别决定显示何种级别的错误或者警告,默认的警告级别是0,用于显示严重错误,您可以通过〖–WARN〗选项指定警告级别,例如,如果想在分析代码时不看到建议级别(4)的提示,可通过警告级别(3)分析代码。
ajaxmin inputfile.js –analyze –warn:3
〖–ENC:IN〗和〖–ENC:OUT〗选项分别用于指定输入文件和输出文件的编码格式,如果不指定它们,默认的输入编码是UTF-8,输出则为ASCII编码,这个参数主要会影响到字符串进行编码的方式。另外,可通过〖–TERM〗选项在压缩后的文件末尾追加一个分号,这有助于以后将更多的文件合并到这个末尾,而不影响之前的代码。使用〖–DEBUG〗选项,将在压缩时移除与调试有关的语句。
<span style=color: blue;>debugger; Web.Debug.Output(<span style=color: maroon;>foo); $Debug.Track(<span style=color: maroon;>bar); Debug.fail(<span style=color: maroon;>debug fail); WAssert(condition, message); <span style=color: blue;>var wnd = <span style=color: blue;>new $Debug.DebugWindow();
上面的代码具有调试信息,在不影响代码语义的情况下,压缩至如下的代码:
<span style=color: blue;>var wnd = ;
若要保留调试语句,可给〖–DEBUG〗指定一个布尔值 true (true, t, yes, y, on, 1) 用于保留调试信息。
ajaxmin –debug:true inputfile.js –out outputfile.js
3、默认的JavaScript压缩方式
Microsoft Ajax Minifier通过语法解析树压缩JavaScript代码,通过压缩这些语法树节点来缩写代码块。默认情况下:
• 删除不必要的空白。 • 删除注释 (除了语句级别""重要""的评论)。 • 删除不必要的分号。 • 移单语句块的大括号。 • 重命名缩短本地变量和函数。 • 确定最佳字符串分隔符 (单引号或双引号)。 • 结合多个相邻的变量声明。 • 删除构造函数上的空参数列表。 • 删除未被使用的局部变量。 • 移除未引用的局部函数。 • 删除无法访问的代码块。
空白和注释的删除是非常易于理解,将多行代码压缩至单行代码是非常有必要的、 删除运算符之间的空格,重要的多行注释的格式 (/ * !......* / )中可申明文件版权信息等,这样的注释将会被保留。分号不是JavaScript语句的结束符,在一个语句块中的最后一条语句并不需要一个分号。例如:
<span style=color: blue;>if (a == 0) { a = 10; alert(a); }
上面的语句块中的最后一个分号将被移除,移除后的结果如下:
<span style=color: blue;>if (a == 0) { a = 10; alert(a) }
这里有一些例外情况,在移除最后一个分号后,在部分版本的Safari浏览器中可能会报错,如果您确实不需要移除最后一个语句的分号,可通过给〖–MAC〗选项设置false值来改变此行为。
去除不必要的大括号也同样简单,如果通常期望的代码 (if、else、for和while) 语句块仅有单个语句,压缩器将删除括号。
<span style=color: blue;>if (a == 0)
上面的代码其实并不需要一个花括号,因为代码块中只有一条语句,压缩后的结果:
<span style=color: blue;>if (a == 0) a = 10;
在JavaScript中,字符串包含在单引号或者双引号之中,如果一个字符串用单引号包含,单引号内的字符串实例必须使用反斜杠进行转义,双引号同理。压缩器在分析字符串语句时,将通过最少的字符选择转义或者不转义引号。例如:
<span style=color: blue;>var g = <span style=color: maroon;>what's his ""name""?;
压缩字符串代码将优先使用更少的字符,因为双引号需要两个转移,单引号只需一个转移,所以压缩后的结果为:
<span style=color: blue;>var g = <span style=color: maroon;>'what's his name?'
编写良好风格的代码,为了可读性和维护性,一般将多个变量的var申明放在同一行上,并用逗号分割它们,这有助于减少下载字节。Microsoft Ajax Minifier将多行的var申明转换成单行申明。
<span style=color: blue;>var a = 0; <span style=color: blue;>var b = <span style=color: maroon;>some string; <span style=color: blue;>var c = 3.14;
压缩后的结果将使用单行申明变量,压缩结果如下:
<span style=color: blue;>var a = 0, b = <span style=color: maroon;>some string, c = 3.14;
<span style=color: #555555;>运算符new用于创建一个新的对象,将对象设置到指定的指针,然后调用构造函数,构造函数实际上是可选的,如果构造函数不传递参数,实际上不需要写一个空括号。例如:
<span style=color: blue;>var img = <span style=color: blue;>new Image();
<span style=color: #555555;>由于构造函数并未传递任何参数,所以空括号可被省略,压缩成如下代码:
<span style=color: blue;>var img = <span style=color: blue;>new Image;
除非使用〖–NEW:KEEP〗选项,否则所有通过new和构造函数创建的对象和数组将都压缩成花括号和方括号申明。
<span style=color: blue;>var obj = <span style=color: blue;>new Object(); <span style=color: blue;>var arr = <span style=color: blue;>new Array(); <span style=color: blue;>var lst = <span style=color: blue;>new Array(1, 2, 3);
<span style=color: #555555;>为了减少代码字节,上面的申明将被压缩成如下的代码,其实它们是等价的:
<span style=color: blue;>var obj = , arr = [], lst = [1, 2, 3];
<span style=color: #555555;>这有一个例外是,当对象和数组的创建,必须为构造函数提供一个参数,便不采用上述行为。
如果if或者else语句块中不存在任何语句,压缩器将修改等价的条件值,重新规划语句块,举个例子:
<span style=color: blue;>if (a >= b) <span style=color: blue;>else { alert(<span style=color: maroon;>a!=b) } <span style=color: blue;>if (foo.bar()) <span style=color: blue;>else { alert(<span style=color: maroon;>not foo.bar()) } <span style=color: blue;>if (!a) <span style=color: blue;>else { alert(<span style=color: maroon;>a) }
<span style=color: #555555;>上面的代码将被AjaxMinifier压缩至如下的代码:
<span style=color: blue;>if (a < b) alert(<span style=color: maroon;>a!=b); <span style=color: blue;>if (!foo.bar()) alert(<span style=color: maroon;>not foo.bar()); <span style=color: blue;>if (a) alert(<span style=color: maroon;>a);
如果使用var在for外部申明变量,AjaxMinifier将合并申明,致使var的申明出现在for条件语句中。
<span style=color: blue;>var i = 5; <span style=color: blue;>for (; i > 0; --i) { alert(i); }
<span style=color: #555555;>压缩后的结果如下,请注意观察var位置的变化:
<span style=color: blue;>for(<span style=color: blue;>var i=5;i>0;--i)alert(i)
当调用一个对象的函数时,可能需要判断对象是否存在指定的函数,通常使用下面的写法:
<span style=color: blue;>if (obj.method) { obj.method(); }
<span style=color: #555555;>AjaxMinnifier将使用更简短的方式压缩上面代码,压缩后的代码与上述代码等价,结果如下:
obj.method && obj.method()
这其实是使用了and运算符的快捷方式,如果第一个表达式计算的结果不为true,将不执行第二个表达式。
4、本地变量和函数重命名
本地函数和变量名重命名为较短的名称,有助于压缩代码体积,但一般情况下,没有任何理由去重命名一个全局变量或者全局函数,因为它们可能被外部文件所引用。AjaxMinifier针对变量和函数使用引用计数和跟踪,这用来删除一些未使用的代码。例如,如果一个函数体内从未使用函数所定义的最后一个参数,这样的参数将会从被删除。
<span style=color: blue;>function DivideTwoNumbers(numerator, denominator, unsedparameter) { <span style=color: blue;>return numerator + denominator; }
<span style=color: #555555;>压缩后的代码如下所示:
<span style=color: blue;>function a(a, b) { <span style=color: blue;>return a / b }
上面的代码在函数体内并未使用形参定义的最后一个参数,它将被删除,另外一种情况是:如果存在未使用的参数,但它不是最后一个参数,这样的参数将不会被删除,因为这会影响调用者使用参数的顺序。
catch关键字提供异常处理机制,通常使用它来判断不同浏览器的行为,在 InternetExplorer 中,在函数范围内可以使用catch语句的e参数,这样的代码在IE中式完全可以接受。
<span style=color: blue;>function foo() { <span style=color: blue;>try { <span style=color: #006400;>// do something that might error } <span style=color: blue;>catch (e) { <span style=color: #006400;>// handle the error } alert(e); }
但在其它浏览器中,将引发一个脚本错误,因为 e 未定义在 catch 语句的外面,这可能看起来像一个微不足道的差异,但是当使用变量重命名功能时,必须确保产生期望的结果。例如,考虑下面的代码:
<span style=color: blue;>var e = <span style=color: maroon;>outer; <span style=color: blue;>function foo() { <span style=color: blue;>try { <span style=color: #006400;>// do something that might error } <span style=color: blue;>catch (e) { <span style=color: #006400;>// handle the error } alert(e); }
这样的写法在大多数浏览器上不会有任何问题,但在IE浏览器上,将始终显示一个为""outer""的错误,catch块被终止执行。这种使得不同的浏览器在处理catch语句的参数e时具有不同的行为,应避免。作为开发人员的辅助手段,如果 Microsoft Ajax Minifier 检测到这种情况,将引发如下错误。
Possible coding error: Ambiguous <span style=color: blue;>catch identifier <span style=color: maroon;>'e'. Cross-browser behavior difference. At line 8, col 10-11: e
当使用switch语句时,我们除了指定的case外,通常使用default关键字处理默认行为,但default后边并不需要一个break语句,因此AjaxMinifier会删除default后边的break语句。有时候经常在同一代码段内多次使用同一段文本,AjaxMinifier将合并它们,举个例子:
<span style=color: blue;>function foo(p) { p[0].style.display = <span style=color: maroon;>block; p[1].style.display = <span style=color: maroon;>block; p[2].style.display = <span style=color: maroon;>block; p[3].style.display = <span style=color: maroon;>block; p[4].style.display = <span style=color: maroon;>block; }
<span style=color: #555555;>经过压缩优化后的代码如下所示:
<span style=color: blue;>function foo(p) { <span style=color: blue;>var a = <span style=color: maroon;>block; p[0].style.display = a; p[1].style.display = a; p[2].style.display = a; p[3].style.display = a; p[4].style.display = a }
5、条件压缩注释
AjaxMinnifier支持对注释的条件压缩,通过在注释中包含@cc_on、@if和@set指令指定要被条件压缩的注释,一般情况下,支持的语句级的条件注释。
<span style=color: blue;>var ie = <span style=color: blue;>false; /@cc_on ie = <span style=color: blue;>true; @/ alert(ie ? <span style=color: maroon;>Internet Explorer : <span style=color: maroon;>NOT Internet Explorer);
因为注释中包含了条件编译条件,所以压缩后的结果如下所示:
<span style=color: blue;>var ie = <span style=color: blue;>false; <span style=color: #006400;>/@cc_onie = true; @/alert(ie ? <span style=color: maroon;>Internet Explorer : <span style=color: maroon;>NOT Internet Explorer)
一般情况下不支持条件编译注释中存在内部表达式,不过,如果条件编译注释中遇到内部表达式,并且注释包含只有一个单条件编译变量引用,将保留注释。别的,它将被忽略。
6、关于变量重命名和Eval语句
在JavaScript中,包含在with和eval中的代码,在执行它们时,一个全局作用域将被推到作用域链的前端。
<span style=color: blue;>var foo = 10; <span style=color: blue;>with (window) { alert(foo); }
这段代码被执行时,由于with关键字将当前作用域设置成了与window相同的全局作用域,我们已无法推断foo变量到底是全局变量还是window的一个属性。eval方法可解析并执行一段字符串代码,同样将改变执行时的作用域链。压缩在分析eval方法时,有可能会产生一些误差,因为字符串是一个弱代码。如果不需要分析和压缩evel方法,〖–EVALS〗选项可设设置此行为,如果将选项设置为〖–EVALS:MAKESAFE 〗分析器将参与对eval方法的分析和压缩。
7、分析您的脚本
因为JavaScrip是一种类型较少的解释性语言,所以开发者无法通过编译的方式在编译时检查代码的可用性和错误,而Microsoft Ajax Minifier却具备检查代码的能力,它是您调试JavaScript的好帮手,通过〖–ANALYZE〗和〖–WARN〗选项可分析不同级别的错误和建议。例如:您可以在部署前分析代码中的错误和BUG,JavaScript是弱语言,由于书写错误,使用未定义的变量在开发期也许未被发现,但这一切都可使用AjaxMinnifier的分析功能帮您搞定。
<span style=color: blue;>function func(p, m) { <span style=color: blue;>for (n = 2; n < m; ++n) { p *= n; } <span style=color: blue;>return p; }
上面这段代码中变量n未被定义就开始使用,这是一种不好的编程习惯,在此处无法确认n属于全局变量还是局部变量,针对这样的代码,AjaxMinnifier的分析功能会告诉您,在整个文件中,那些变量是未定义却已被使用的。JavaScript只具有全局作用域和函数级作用域,不像其它语言(C语言和C#语言)具有块级作用域,举个例子,这段代码:
<span style=color: blue;>function func() { <span style=color: blue;>var n, a = <span style=color: maroon;>outer; <span style=color: blue;>for (n = 0; n < 10; ++n) { <span style=color: blue;>var a = n; <span style=color: #006400;>// do something else } alert(a); }
在C语言或者C#语言中,如果编写类似上面的代码,字符串“outer”将被传递给alert函数进行打印,因为for语句块中的a和函数级别的a是两个不同的变量,函数级作用域和块级作用域是两个不同的作用域,但在JavaScript中执行这段代码,却将数字10传递给alert函数,因为JavaScript只具有函数级作用域a,for语句块中定义的a将覆盖函数级作用域。为了提高可读性和避免一些不必要的BUG,AjaxMinnifier的分析功能也会支持针对这种情况分析。
8、指定外部全局变量
在使用AjaxMinnifier的分析功能时,由于您的脚本中引用了其它模块中的变量,这可能会抛出一个变量未定义的异常,不过您可以使用〖–GLOBAL〗选项消除这些错误。 例如,如果您知道您的代码中引用了XMLSerializer、Msn和HelperFunctio变量,但它们在另一个文件中定义,您可以像这样运行AjaxMinnifier命令行用于忽略这些引用。
ajaxmin –analyze inputfile.js –global:XMLSerializer,Msn,HelperFunction
9、CSS压缩介绍
AjaxMinnifier不仅支持压缩JavaScript,同时也支持针对CSS样式表的压缩和优化,CSS的压缩主要包括删除空白和大多数注释,但标记important的注释将永远被保留,AjaxMinnifier还会优化CSS中的颜色值,例如,rgb(255,128,0) 将改为压缩成""#ff8000"",#ff9900""压缩为#f90"",理想情况下CSS压缩工具能够消除冗余规则,但这具有风险,为了避免风险,此工具不提供消除冗余功能。
10、默认CSS压缩规则
默认情况下AjaxMinnifier针对CSS的压缩将采用如下的设置:
• 删除所有无关紧要的空白。 • 移除所有注释。 • 删除所有不必要的分号。 • 缩短颜色值。 • 删除前导为0的整数表示形式。 • 利用W3C严格颜色名称来进一步减少生成的代码。
11、CSS压缩器选项
使用AjaxMinnifier压缩CSS文件时,可指定〖–CSS〗选项,〖–COLORS〗选项用于如何压缩颜色值,如果使用〖–COLORS:HEX〗选项,具有名称的颜色将全部替换为16进制表示的RGB颜色值。如果使用默认选项,当颜色名称的长度小于16进制颜色值时,将使用W3C指定的颜色名称,有17种W3C规定的颜色名分别是:aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white和yellow。
默认情况下,压缩器将删除所有的CSS注释,不过您可以通过〖–COMMENTS〗选项改变默认行为,默认情况下〖–COMMENTS:NONE〗将移除所有注释,如果要保留所有注释请使用〖–COMMENTS:ALL〗选项,如果要保留针对浏览器兼容的HACKS,请使用〖-COMMENTS:HACKS〗选项。如果要保留CSS最后一个规则的分号,请使用〖–TERM〗选项。
微软官方目前也提供AjaxMin.dll和AjaxMinTask.dll两个程序集,AjaxMin.dll提供压缩代码的C#接口,可在你的项目中编写C#代码调用压缩接口,AjaxMinTask.dll是VisualStudio生成任务的一个插件,您可以根据官方的说明配置您的解决方案,当你生成项目时,它会自动查找项目中的JS和CSS文件,然后生成的压缩后的min版本。
以上就是来自零度的翻译,更多内容,您可以通过下面的按钮访问微软官网下载工具、开源代码和查阅详细的帮助指南。
零度下载"