Generally, when we start gzip, we rarely start gzip for html, because the current html is dynamic and does not use browser cache. If gzip is enabled, each request needs to be compressed, which will consume more server resources and start for js and css. Gzip is better because both js and css use caching. I personally think that the biggest benefit of compressing HTML is that it can make a lot of money. As long as you write it once, it can be used by all programs in the future without adding any additional development work.
In the article "Merge, Compress, and Cache Management of JS and CSS", I mentioned a component I wrote that automatically merges and compresses JS and CSS, and adds a version number. This time, the function of compressing html is also added to this component. The process is very simple, that is, scanning all html and jsp (aspx) for compression when the program starts (contextInitialized or Application_Start).
Compression notes:
The implementation method is mainly to use regular expressions to search and replace. When compressing HTML, you should mainly pay attention to the following points:
1. The content format in the pre, textarea tag needs to be retained and cannot be compressed.
2. When removing html comments, some comments cannot be removed, such as:
3. Pay attention when compressing comments in embedded js, because comment symbols may appear in the string, for example: var url = "http://www.cnblogs.com"; // The preceding // is not a comment
When removing JS line breaks, you cannot directly follow the action content. Spaces are required. Consider the following code:
else
return;
If there are no spaces, it becomes elsereturn.
4. In jsp (aspx), it is very likely that <% %> will be used to embed some server code. At this time, it also needs to be processed separately. The processing method of comments inside is the same as that of js.
Source code:
The following is the source code of java implementation. You can also click here to download the code. I believe everyone can understand it and it can be easily changed into net code:
import java.io.StringReader;
java.io.StringWriter 가져오기;
java.util.* 가져오기;
java.util.regex.* 가져오기;
/*******************************************
* jsp, html 코드 압축 , 모든 공백 및 개행 문자 제거
* @author bearrui(ak-47)
* @version 0.1
* @date 2010-5-13
********* *** *********************************/
public class HtmlCompressor {
private static String tempPreBlock = "%%%HTMLCOMPRESS~PRE&&&";
비공개 정적 문자열 tempTextAreaBlock = "%%%HTMLCOMPRESS~TEXTAREA&&&";
private static String tempScriptBlock = "%%%HTMLCOMPRESS~SCRIPT&&&";
private static String tempStyleBlock = "%%%HTMLCOMPRESS~STYLE&&&";
private static String tempJspBlock = "%%%HTMLCOMPRESS~JSP&&&";
비공개 정적 패턴 commentPattern = Pattern.compile("", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
비공개 정적 패턴 itsPattern = Pattern.compile(">\s ?<", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
비공개 정적 패턴 prePattern = Pattern.compile("
]*?>.*?
", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
비공개 정적 패턴 taPattern = Pattern.compile("
", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
비공개 정적 패턴 jspPattern = Pattern.compile("<%([^-@][\w\W]*?)%>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
// <스크립트>
비공개 정적 패턴 scriptPattern = Pattern.compile("(?:", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
비공개 정적 패턴 stylePattern = Pattern.compile("", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
// 单行注释,
private static Pattern signleCommentPattern = Pattern.compile("//.*");
// 字符串匹配
private static Pattern stringPattern = Pattern.compile("("[^"\n]*?"|'[^'\n]*?')");
// 트리밍 작업 공간
private static Pattern TrimPattern = Pattern.compile("\n\s*",Pattern.MULTILINE);
private static Pattern TrimPattern2 = Pattern.compile("\s*\ r",Pattern.MULTILINE);
// 多行注释
private static Pattern multiCommentPattern = Pattern.compile("/\*.*?\*/", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern. MULTILINE);
private static String tempSingleCommentBlock = "%%%HTMLCOMPRESS~SINGLECOMMENT&&&" // //位符
private static String tempMulitCommentBlock1 = "%%%HTMLCOMPRESS~MULITCOMMENT1&&&" // /*符
private static String tempMulitCommentBlock2 = "%%%HTMLCOMPRESS~MULITCOMMENT2&&&" // */·位符
public static String 압축(String html)에서 예외 발생 {
if(html == null || html.length() == 0) {
return html;
List
preBlocks = new ArrayList(); 배열목록()
목록 scriptBlocks = new ArrayList();
목록 styleBlocks = new ArrayList();
목록 jspBlocks = new ArrayList();
문자열 결과 = html;
//인라인 자바 코드 유지
Matcher jspMatcher = jspPattern.matcher(result);
while(jspMatcher.find()) {
jspBlocks.add(jspMatcher.group(0));
}
결과 = jspMatcher.replaceAll(tempJspBlock);
//PRE 태그 유지
Matcher preMatcher = prePattern.matcher(result);
while(preMatcher.find()) {
preBlocks.add(preMatcher.group(0));
}
결과 = preMatcher.replaceAll(tempPreBlock);
//TEXTAREA 태그 유지
Matcher taMatcher = taPattern.matcher(result);
while(taMatcher.find()) {
taBlocks.add(taMatcher.group(0));
}
결과 = taMatcher.replaceAll(tempTextAreaBlock);
//SCRIPT 태그 유지
Matcher scriptMatcher = scriptPattern.matcher(result);
while(scriptMatcher.find()) {
scriptBlocks.add(scriptMatcher.group(0));
}
결과 = scriptMatcher.replaceAll(tempScriptBlock);
// 인라인 CSS를 처리하지 않음
Matcher styleMatcher = stylePattern.matcher(result);
while(styleMatcher.find()) {
styleBlocks.add(styleMatcher.group(0));
}
결과 = styleMatcher.replaceAll(tempStyleBlock);
//순수 HTML 처리
result = processHtml(result);
//보존된 블록 처리
result = processPreBlocks(result, preBlocks);
결과 = processTextareaBlocks(결과, taBlocks);
결과 = processScriptBlocks(결과, scriptBlocks);
결과 = processStyleBlocks(결과, styleBlocks);
결과 = processJspBlocks(결과, jspBlocks);
preBlocks = taBlocks = scriptBlocks = styleBlocks = jspBlocks = null;
결과 반환.trim();
}
private static String processHtml(String html) {
String result = html;
//댓글 제거
// if(removeComments) {
result = commentPattern.matcher(result).replaceAll("");
// }
//태그 간 공백 제거
// if(removeIntertagSpaces) {
result = itsPattern.matcher(result).replaceAll("><");
// }
//여러 공백 문자 제거
// if(removeMultiSpaces) {
result = result.replaceAll("\s{2,}"," ");
// }
결과 반환;
}
private static String processJspBlocks(String html, List 블록){
String result = html;
for(int i = 0; i blocks.set(i,compressJsp(blocks.get(i)));
}
//보존된 블록을 다시 넣기
while(result.contains(tempJspBlock)) {
result = result.replaceFirst(tempJspBlock, Matcher.quoteReplacement(blocks.remove(0)));
}
결과 반환;
}
비공개 정적 문자열 processPreBlocks(String html, List 블록)에서 예외 발생 {
String result = html;
//보존된 블록을 다시 넣기
while(result.contains(tempPreBlock)) {
result = result.replaceFirst(tempPreBlock, Matcher.quoteReplacement(blocks.remove(0)));
}
결과 반환;
}
비공개 정적 문자열 processTextareaBlocks(String html, List 블록)에서 예외 발생 {
String result = html;
//보존된 블록을 다시 넣기
while(result.contains(tempTextAreaBlock)) {
result = result.replaceFirst(tempTextAreaBlock, Matcher.quoteReplacement(blocks.remove(0)));
}
결과 반환;
}
비공개 정적 문자열 processScriptBlocks(String html, List 블록)에서 예외 발생 {
String result = html;
// if(compressJavaScript) {
for(int i = 0; i blocks.set(i,compressJavaScript(blocks.get(i)) );
}
// }
//보존된 블록을 다시 넣기
while(result.contains(tempScriptBlock)) {
result = result.replaceFirst(tempScriptBlock, Matcher.quoteReplacement(blocks.remove) (0)));
}
결과 반환;
}
비공개 정적 문자열 processStyleBlocks(String html, List 블록)에서 예외 발생 {
String result = html;
// if(compressCss) {
for(int i = 0; i blocks.set(i,compressCssStyles(blocks.get(i)) );
}
// }
//보존된 블록을 다시 넣기
while(result.contains(tempStyleBlock)) {
result = result.replaceFirst(tempStyleBlock, Matcher.quoteReplacement(blocks.remove) (0)));
}
결과 반환;
}
private static String 압축Jsp(String source) {
//블록이 비어 있지 않은지 확인
Matcher jspMatcher = jspPattern.matcher(source);
if(jspMatcher.find()) {
String result = 압축JspJs(jspMatcher.group(1));
return (new StringBuilder(source.substring(0, jspMatcher.start(1))).append(result).append(source.substring(jspMatcher.end(1)))).toString();
} else {
반환 소스;
}
}
private static String 압축JavaScript(String source) {
//블록이 비어 있지 않은지 확인
Matcher scriptMatcher = scriptPattern.matcher(source);
if(scriptMatcher.find()) {
String result = 압축JspJs(scriptMatcher.group(1));
return (new StringBuilder(source.substring(0, scriptMatcher.start(1))).append(result).append(source.substring(scriptMatcher.end(1)))).toString();
} else {
반환 소스;
}
}
private static String 압축CssStyles(String source) {
//블록이 비어 있지 않은지 확인
Matcher styleMatcher = stylePattern.matcher(source);
if(styleMatcher.find()) {
// 去掉注释,换行
String result= multiCommentPattern.matcher(styleMatcher.group(1)).replaceAll("");
결과 = TrimPattern.matcher(result).replaceAll("");
결과 = TrimPattern2.matcher(result).replaceAll("");
return (new StringBuilder(source.substring(0, styleMatcher.start(1))).append(result).append(source.substring(styleMatcher.end(1)))).toString();
} else {
반환 소스;
}
}
private static String extractJspJs(String source){
String result = source;
// 문자열에 주석이 나타날 수 있으므로 문자열의 특수 문자를 먼저 제거해야 합니다
Matcher stringMatcher = stringPattern. matcher(result);
while(stringMatcher.find()){
String tmpStr = stringMatcher.group(0)
if(tmpStr.indexOf("//") != -1 || tmpStr.indexOf("/*") != -1 || tmpStr.indexOf("*/") != -1){
String blockStr = 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, "*/");
return result;
}
}
위 방법을 사용하고 프로그램을 실행한 후, 각 페이지의 소스코드를 보면 1줄로 되어있는데, 사용해보면 그래도 비용을 지불해야 하는 경우가 있나요? 몇 가지 문제에 주의:
1. js를 삽입할 때 원래 yuicompressor를 호출하여 JS를 압축하려고 했습니다. 먼저 js를 컴파일하여 합법적인지 확인합니다. embed는 var now = <%=DateTime.now %>와 같은 일부 서버측 코드를 사용할 수 있으며, 이러한 코드는 컴파일에 실패하므로 yuicompressor를 사용할 수 없습니다.
결국 압축된 JS 코드를 제가 직접 작성해야 했습니다. 제 글이 상대적으로 거칠어서 아직 해결해야 할 문제가 있습니다. 즉, 개발자가 js 코드 문장 뒤에 세미콜론을 추가하지 않으면, 압축된 JS 코드가 한 줄의 질문으로 압축될 가능성이 매우 높습니다. 따라서 이것을 사용할 때 각 명령문 끝에 세미콜론이 있어야 함을 확인해야 합니다.
2. 프로그램 시작 시 모든 jsp(aspx)가 압축되기 때문에 동적으로 생성된 html은 사용자가 요청할 때 압축될 수 없습니다.