web前端:Web高性能开发之疯狂的HTML压缩

    作者:BearRui(AK-47) 更新于: 2020-12-04 13:14:34

    Web开发

      层叠样式表(英文全称:CascadingStyleSheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。

      摘要:在“JS、CSS的合并、压缩、缓存管理”一文中说到自己写过的1个自动合并、压缩JS,CSS,并添加版本号的组件。这次把压缩html的功能也加入到该组件中,流程很简单,就是在程序启动(contextInitializedorApplication_Start)的时候扫描所有html,jsp(aspx)进行压缩。

      前言:

      上一篇随笔中网友skyaspnet问我如何压缩HTML,当时回答是推荐他使用gzip,后来想想,要是能把所有的html,jsp(aspx)在运行前都压缩成1行未免不是一件好事啊。一般我们启动gzip都比较少对html启动gzip,因为现在的html都是动态的,不会使用浏览器缓存,而启用gzip的话每次请求都需要压缩,会比较消耗服务器资源,对js,css启动gzip比较好是因为js,css都会使用缓存。我个人觉得的压缩html的最大好处就是一本万利,只要写好了一次,以后所有程序都可以使用,不会增加任何额外的开发工作。

      在JS、CSS的合并、压缩、缓存管理一文中说到自己写过的1个自动合并、压缩JS,CSS,并添加版本号的组件。这次把压缩html的功能也加入到该组件中,流程很简单,就是在程序启动(contextInitializedorApplication_Start)的时候扫描所有html,jsp(aspx)进行压缩。

      压缩的注意事项:

      实现的方式主要是用正则表达式去查找,替换。在html压缩的时候,主要要注意下面几点:

      1.pre,textarea标签里面的内容格式需要保留,不能压缩。

      2.去掉html注释的时候,有些注释是不能去掉的,比如:!--[ifIE6].....![endif]--

      3.压缩嵌入式js中的注释要注意,因为可能注释符号会出现在字符串中,比如:varurl="http://www.cnblogs.com";//前面的//不是注释

      去掉JS换行符的时候,不能直接跟一下行动内容,需要有空格,考虑下面的代码:

      else

      return;

      如果不带空格,则变成elsereturn。

      4.jsp(aspx)中很有可能会使用%%嵌入一些服务器代码,这个时候也需要单独处理,里面注释的处理方法跟js的一样。

      源代码:

      下面是java实现的源代码,也可以猛击此处下载该代码,相信大家都看的懂,也很容易改成net代码:

      importjava.io.StringReader;

      importjava.io.StringWriter;

      importjava.util.*;

      importjava.util.regex.*;

      /*******************************************

      *压缩jsp,html中的代码,去掉所有空白符、换行符

      *@authorbearrui(ak-47)

      *@version0.1

      *@date2010-5-13

      *******************************************/

      publicclassHtmlCompressor{

      privatestaticStringtempPreBlock="%%%HTMLCOMPRESS~PRE&&&";

      privatestaticStringtempTextAreaBlock="%%%HTMLCOMPRESS~TEXTAREA&&&";

      privatestaticStringtempScriptBlock="%%%HTMLCOMPRESS~SCRIPT&&&";

      privatestaticStringtempStyleBlock="%%%HTMLCOMPRESS~STYLE&&&";

      privatestaticStringtempJspBlock="%%%HTMLCOMPRESS~JSP&&&";

      privatestaticPatterncommentPattern=Pattern.compile("!--\\\\s*[^\\\\[].*?--",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticPatternitsPattern=Pattern.compile("\\\\s+?",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticPatternprePattern=Pattern.compile("pre[^]*?.*?/pre",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticPatterntaPattern=Pattern.compile("textarea[^]*?.*?/textarea",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticPatternjspPattern=Pattern.compile("%([^-@][\\\\w\\\\W]*?)%",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      //script/script

      privatestaticPatternscriptPattern=Pattern.compile("(?:script\\\\s*|scripttype=['\\"]text/Javascript['\\"]\\\\s*)(.*?)/script",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticPatternstylePattern=Pattern.compile("style[^()]*?(.+)/style",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      //单行注释,

      privatestaticPatternsignleCommentPattern=Pattern.compile("//.*");

      //字符串匹配

      privatestaticPatternstringPattern=Pattern.compile("(\\"[^\\"\\\\n]*?\\"|'[^'\\\\n]*?')");

      //trim去空格和换行符

      privatestaticPatterntrimPattern=Pattern.compile("\\\\n\\\\s*",Pattern.MULTILINE);

      privatestaticPatterntrimPattern2=Pattern.compile("\\\\s*\\\\r",Pattern.MULTILINE);

      //多行注释

      privatestaticPatternmultiCommentPattern=Pattern.compile("/\\\\*.*?\\\\*/",Pattern.DOTALL|Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);

      privatestaticStringtempSingleCommentBlock="%%%HTMLCOMPRESS~SINGLECOMMENT&&&";////占位符

      privatestaticStringtempMulitCommentBlock1="%%%HTMLCOMPRESS~MULITCOMMENT1&&&";///*占位符

      privatestaticStringtempMulitCommentBlock2="%%%HTMLCOMPRESS~MULITCOMMENT2&&&";//*/占位符

      publicstaticStringcompress(Stringhtml)throwsException{

      if(html==null||html.length()==0){

      returnhtml;

      }

      ListStringpreBlocks=newArrayListString();

      ListStringtaBlocks=newArrayListString();

      ListStringscriptBlocks=newArrayListString();

      ListStringstyleBlocks=newArrayListString();

      ListStringjspBlocks=newArrayListString();

      Stringresult=html;

      //preserveinlinejavacode

      MatcherjspMatcher=jspPattern.matcher(result);

      while(jspMatcher.find()){

      jspBlocks.add(jspMatcher.group(0));

      }

      result=jspMatcher.replaceAll(tempJspBlock);

      //preservePREtags

      MatcherpreMatcher=prePattern.matcher(result);

      while(preMatcher.find()){

      preBlocks.add(preMatcher.group(0));

      }

      result=preMatcher.replaceAll(tempPreBlock);

      //preserveTEXTAREAtags

      MatchertaMatcher=taPattern.matcher(result);

      while(taMatcher.find()){

      taBlocks.add(taMatcher.group(0));

      }

      result=taMatcher.replaceAll(tempTextAreaBlock);

      //preserveSCRIPTtags

      MatcherscriptMatcher=scriptPattern.matcher(result);

      while(scriptMatcher.find()){

      scriptBlocks.add(scriptMatcher.group(0));

      }

      result=scriptMatcher.replaceAll(tempScriptBlock);

      //don'tprocessinlinecss

      MatcherstyleMatcher=stylePattern.matcher(result);

      while(styleMatcher.find()){

      styleBlocks.add(styleMatcher.group(0));

      }

      result=styleMatcher.replaceAll(tempStyleBlock);

      //processpurehtml

      result=processHtml(result);

      //processpreservedblocks

      result=processPreBlocks(result,preBlocks);

      result=processTextareaBlocks(result,taBlocks);

      result=processScriptBlocks(result,scriptBlocks);

      result=processStyleBlocks(result,styleBlocks);

      result=processJspBlocks(result,jspBlocks);

      preBlocks=taBlocks=scriptBlocks=styleBlocks=jspBlocks=null;

      returnresult.trim();

      }

      privatestaticStringprocessHtml(Stringhtml){

      Stringresult=html;

      //removecomments

      //if(removeComments){

      result=commentPattern.matcher(result).replaceAll("");

      //}

      //removeinter-tagspaces

      //if(removeIntertagSpaces){

      result=itsPattern.matcher(result).replaceAll("");

      //}

      //removemultiwhitespacecharacters

      //if(removeMultiSpaces){

      result=result.replaceAll("\\\\s{2,}","");

      //}

      returnresult;

      }

      privatestaticStringprocessJspBlocks(Stringhtml,ListStringblocks){

      Stringresult=html;

      for(inti=0;iblocks.size();i++){

      blocks.set(i,compressJsp(blocks.get(i)));

      }

      //putpreservedblocksback

      while(result.contains(tempJspBlock)){

      result=result.replaceFirst(tempJspBlock,Matcher.quoteReplacement(blocks.remove(0)));

      }

      returnresult;

      }

      privatestaticStringprocessPreBlocks(Stringhtml,ListStringblocks)throwsException{

      Stringresult=html;

      //putpreservedblocksback

      while(result.contains(tempPreBlock)){

      result=result.replaceFirst(tempPreBlock,Matcher.quoteReplacement(blocks.remove(0)));

      }

      returnresult;

      }

      privatestaticStringprocessTextareaBlocks(Stringhtml,ListStringblocks)throwsException{

      Stringresult=html;

      //putpreservedblocksback

      while(result.contains(tempTextAreaBlock)){

      result=result.replaceFirst(tempTextAreaBlock,Matcher.quoteReplacement(blocks.remove(0)));

      }

      returnresult;

      }

      privatestaticStringprocessScriptBlocks(Stringhtml,ListStringblocks)throwsException{

      Stringresult=html;

      //if(compressJavaScript){

      for(inti=0;iblocks.size();i++){

      blocks.set(i,compressJavaScript(blocks.get(i)));

      }

      //}

      //putpreservedblocksback

      while(result.contains(tempScriptBlock)){

      result=result.replaceFirst(tempScriptBlock,Matcher.quoteReplacement(blocks.remove(0)));

      }

      returnresult;

      }

      privatestaticStringprocessStyleBlocks(Stringhtml,ListStringblocks)throwsException{

      Stringresult=html;

      //if(compressCss){

      for(inti=0;iblocks.size();i++){

      blocks.set(i,compressCssStyles(blocks.get(i)));

      }

      //}

      //putpreservedblocksback

      while(result.contains(tempStyleBlock)){

      result=result.replaceFirst(tempStyleBlock,Matcher.quoteReplacement(blocks.remove(0)));

      }

      returnresult;

      }

      privatestaticStringcompressJsp(Stringsource){

      //checkifblockisnotempty

      MatcherjspMatcher=jspPattern.matcher(source);

      if(jspMatcher.find()){

      Stringresult=compressJspJs(jspMatcher.group(1));

      return(newStringBuilder(source.substring(0,jspMatcher.start(1))).append(result).append(source.substring(jspMatcher.end(1)))).toString();

      }else{

      returnsource;

      }

      }

      privatestaticStringcompressJavaScript(Stringsource){

      //checkifblockisnotempty

      MatcherscriptMatcher=scriptPattern.matcher(source);

      if(scriptMatcher.find()){

      Stringresult=compressJspJs(scriptMatcher.group(1));

      return(newStringBuilder(source.substring(0,scriptMatcher.start(1))).append(result).append(source.substring(scriptMatcher.end(1)))).toString();

      }else{

      returnsource;

      }

      }

      privatestaticStringcompressCssStyles(Stringsource){

      //checkifblockisnotempty

      MatcherstyleMatcher=stylePattern.matcher(source);

      if(styleMatcher.find()){

      //去掉注释,换行

      Stringresult=multiCommentPattern.matcher(styleMatcher.group(1)).replaceAll("");

      result=trimPattern.matcher(result).replaceAll("");

      result=trimPattern2.matcher(result).replaceAll("");

      return(newStringBuilder(source.substring(0,styleMatcher.start(1))).append(result).append(source.substring(styleMatcher.end(1)))).toString();

      }else{

      returnsource;

      }

      }

      privatestaticStringcompressJspJs(Stringsource){

      Stringresult=source;

      //因注释符合有可能出现在字符串中,所以要先把字符串中的特殊符好去掉

      MatcherstringMatcher=stringPattern.matcher(result);

      while(stringMatcher.find()){

      StringtmpStr=stringMatcher.group(0);

      if(tmpStr.indexOf("//")!=-1||tmpStr.indexOf("/*")!=-1||tmpStr.indexOf("*/")!=-1){

      StringblockStr=tmpStr.replaceAll("//",tempSingleCommentBlock).replaceAll("/\\\\*",tempMulitCommentBlock1)

      .replaceAll("\\\\*/",tempMulitCommentBlock2);

      result=result.replace(tmpStr,blockStr);

      }

      }

      //去掉注释

      result=signleCommentPattern.matcher(result).replaceAll("");

      result=multiCommentPattern.matcher(result).replaceAll("");

      result=trimPattern2.matcher(result).replaceAll("");

      result=trimPattern.matcher(result).replaceAll("");

      //恢复替换掉的字符串

      result=result.replaceAll(tempSingleCommentBlock,"//").replaceAll(tempMulitCommentBlock1,"/*")

      .replaceAll(tempMulitCommentBlock2,"*/");

      returnresult;

      }

      }

      使用注意事项:

      使用了上面方法后,再运行程序,是不是发现每个页面查看源代码的时候都变成1行啦,还不错吧,但是在使用的时候还是要注意一些问题:

      1.嵌入js本来想调用yuicompressor来压缩,yuicompressor压缩JS前,会先编译js是否合法,因我们嵌入的js中可能很多会用到一些服务器端代码,比如varnow=%=DateTime.now%,这样的代码会编译不通过,所以无法使用yuicompressor。

      最后只能自己写压缩JS代码,自己写的比较粗燥,所以有个问题还解决,就是如果开发人员在一句js代码后面没有加分号的话,压缩成1行就很有可能出问题。所以使用这个需要保证每条语句结束后都必须带分号。

      2.因为是在程序启动的时候压缩所有jsp(aspx),所以如果是用户请求的时候动态产生的html就无法压缩。

      CSS能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。

课课家教育

未登录

1