论坛首页 Web前端技术论坛

Javascript开发之js压缩篇

浏览 10798 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2009-04-03   最后修改:2009-04-12
Extjs可以说是目前最优秀的js开发库了。除了那个蹩脚的GPLV3授权。
但是使用中遇到的第一个问题就是,Extjs那庞大的个头。想办法压缩ExtJS的大小成了首先要解决的问题。
谈谈我的解决方法,欢迎拍砖。突然发现前面那个广告贴被锁了 

1、压缩混淆
   除了ExtJS的大个子以外,引用的很多其他的js库,项目中自己的js文件等等。采用OPOA组件式开发最后一定会增大js文件的总量。所以项目后期要对这些文件进行压缩合并。现在流行的js压缩工具有很多,如packer,jsMin,Esc,JSA,yui-compressor等等。经过实际使用我选的是yui-compressor.
   yui-compressor项目地址:http://developer.yahoo.com/yui/compressor/
下载后得到一个java开发的jar包。使用方法基于命令行:
   java -jar yuicompressor-x.y.z.jar [options] [input file]
开发中的js文件不可能一个个的手动压缩,这里用到了ANT。在项目构建中可以替你完成以上任务。
<property name="yuicompressor" value="${tools}/yuicompressor-2.3.6.jar" />
      <apply executable="java" parallel="false" verbose="true" dest="${dist}/WebRoot">
          <fileset dir="${dist}/WebRoot">
	          <include name="modules/*.js" />
	          <include name="js/*.js" />
          </fileset>
          <arg line="-jar" />
          <arg path="${yuicompressor}" />
          <arg line="--type js --charset UTF-8 -o" />
          <targetfile />
          <mapper type="glob" from="*.js" to="*-m.js" />
      </apply>
      <concat destfile="${dist}/WebRoot/js/base.js" encoding="UTF-8" >
          <fileset dir="${dist}/WebRoot/js">
              <include name="*-m.js" />
          </fileset>
      </concat>
      <concat destfile="${dist}/WebRoot/modules/modules.js" encoding="UTF-8" >
          <fileset dir="${dist}/WebRoot/modules">
              <include name="*-m.js" />
          </fileset>
      </concat>
      <delete>
          <fileset dir="${dist}/WebRoot">
              <include name="js/*.js"/>
              <include name="modules/*.js"/>
              <exclude name="modules/modules.js" />
              <exclude name="js/base.js" />
          </fileset>
      </delete>



2、gzip压缩。
   坛子里讨论的gzip压缩有2种,
   一是有的容器(服务器)提供的功能,但这个局限于特定容器。比如apache+tomcat或者resin-pro版。
   二是部署前手动gzip压缩,配合servlet过滤器使用,这个能实现gzip功能,但是降低了灵活性。
   我自己用的是自己的实现,采用gzip servlet filter实现。下面是我的代码参考网上内容.

package sh.blog.util.web.filter;

import java.io.IOException;

import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;

public class CompressedStream extends ServletOutputStream {
	
	private ServletOutputStream out;
	private GZIPOutputStream    gzip;

	/**
	 * 指定压缩缓冲流
	 * @param 输出流到压缩
	 * @throws IOException if an error occurs with the {@link GZIPOutputStream}.
	 */
	public CompressedStream(ServletOutputStream out) throws IOException {
		this.out = out;
		reset();
	}

	/** @see ServletOutputStream * */
	public void close() throws IOException {
		gzip.close();
	}

	/** @see ServletOutputStream * */
	public void flush() throws IOException {
		gzip.flush();
	}

	/** @see ServletOutputStream * */
	public void write(byte[] b) throws IOException {
		write(b, 0, b.length);
	}

	/** @see ServletOutputStream * */
	public void write(byte[] b, int off, int len) throws IOException {
		gzip.write(b, off, len);
	}

	/** @see ServletOutputStream * */
	public void write(int b) throws IOException {
		gzip.write(b);
	}

	/**
	 * Resets the stream.
	 * 
	 * @throws IOException if an I/O error occurs.
	 */
	public void reset() throws IOException {
		gzip = new GZIPOutputStream(out);
	}
}


package sh.blog.util.web.filter;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class CompressionResponse extends HttpServletResponseWrapper {
	
	protected HttpServletResponse response;
	private ServletOutputStream out;
	private CompressedStream compressedOut;
	private PrintWriter writer;
	protected int contentLength;

	/**
	 * 创建一个新的被压缩响应给HTTP
	 * 
	 * @param response the HTTP response to wrap.
	 * @throws IOException if an I/O error occurs.
	 */
	public CompressionResponse(HttpServletResponse response) throws IOException {
		super(response);
		this.response = response;
		compressedOut = new CompressedStream(response.getOutputStream());
	}

	/**
	 * Ignore attempts to set the content length since the actual content length
	 * will be determined by the GZIP compression.
	 * 
	 * @param len the content length
	 */
	public void setContentLength(int len) {
		contentLength = len;
	}

	/** @see HttpServletResponse * */
	public ServletOutputStream getOutputStream() throws IOException {
		if (null == out) {
			if (null != writer) {
				throw new IllegalStateException("getWriter() has already been called on this response.");
			}
			out = compressedOut;
		}
		return out;
	}

	/** @see HttpServletResponse * */
	public PrintWriter getWriter() throws IOException {
		if (null == writer) {
			if (null != out) {
				throw new IllegalStateException("getOutputStream() has already been called on this response.");
			}
			writer = new PrintWriter(compressedOut);
		}
		return writer;
	}

	/** @see HttpServletResponse * */
	public void flushBuffer() {
		try {
			if (writer != null) {
				writer.flush();
			} else if (out != null) {
				out.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/** @see HttpServletResponse * */
	public void reset() {
		super.reset();
		try {
			compressedOut.reset();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/** @see HttpServletResponse * */
	public void resetBuffer() {
		super.resetBuffer();
		try {
			compressedOut.reset();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Finishes writing the compressed data to the output stream. Note: this
	 * closes the underlying output stream.
	 * 
	 * @throws IOException if an I/O error occurs.
	 */
	public void close() throws IOException {
		compressedOut.close();
	}
}

/**
 * 如果浏览器支持解压缩,则压缩该代码
 * @throws IOException ServletException if an error occurs with the {@link GZIPOutputStream}.
 * 如果需要开启该过滤器,在web.xml中加入此代码
    <filter>
      <filter-name>gzip</filter-name>
      <filter-class>com.strongit.finance.gzip</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>gzip</filter-name>
      <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
 */

package sh.blog.util.web.filter;

import java.io.IOException;

import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class CompressionFilter implements Filter {

	protected Log log = LogFactory.getFactory().getInstance(this.getClass().getName());

    @SuppressWarnings("unchecked")
	public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
    	
        boolean compress = false;
        if (request instanceof HttpServletRequest){
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            Enumeration headers = httpRequest.getHeaders("Accept-Encoding");
            while (headers.hasMoreElements()){
                String value = (String) headers.nextElement();
                if (value.indexOf("gzip") != -1){
                    compress = true;
                }
            }
        }
        
        if (compress){//如果浏览器支持则压缩
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.addHeader("Content-Encoding", "gzip");
            CompressionResponse compressionResponse= new CompressionResponse(httpResponse);
            chain.doFilter(request, compressionResponse);
            compressionResponse.close();
        }
        else{//如果浏览器不支持则不压缩
            chain.doFilter(request, response);
        }
    }
    
    
    public void init(FilterConfig config) throws ServletException {
        
    }

    public void destroy(){
        
    }
}


实际使用的效果以http://demo.slivercrm.cn:8084为例

登陆窗口:





首页:




  • 大小: 65.9 KB
  • 大小: 19.4 KB
  • 大小: 23.9 KB
  • 大小: 29.9 KB
   发表时间:2009-04-10  
我使用的静态压缩js/css也要设置Content-Encoding, 否则有些浏览器无法判断文件是否已经压缩, 解释出错. 而下发时需要通过Tomcat filter来设置Content-Encoding, 占用资源. 不知道楼主有没有好的方法?
0 请登录后投票
   发表时间:2009-04-11  
占用资源其实不是很大,第一次可能慢一些。第二次就缓存了。
静态压缩你可以搜索本坛badqiu的一篇文章,提供了过滤器实现。
0 请登录后投票
   发表时间:2009-08-16  
楼主,上面图片显示压缩后文件大小的工具是什么?
0 请登录后投票
   发表时间:2009-08-16  
406656983 写道
楼主,上面图片显示压缩后文件大小的工具是什么?

Yslow
0 请登录后投票
   发表时间:2010-01-18  
给你提点建议,你自己实现的compress不是很好哦,
1. CompressedStream 没有 buffer ?直接都写入stream不太好
2.wrapper getWriter 没有考虑字符编码问题?,request.getCharacterEncoding可以用用
3.filter 异常处理下

try{
chain.doFilter(request, compressionResponse);
}
finally{ 
            compressionResponse.close(); 
}



网上看过已有的实现是比较好的,你可以飕飕
0 请登录后投票
   发表时间:2010-01-18  
引用

2.wrapper getWriter 没有考虑字符编码问题?,request.getCharacterEncoding可以用用

sorry , 是 response.getCharacterEncoding 不为null有的话要考虑一下
0 请登录后投票
   发表时间:2010-01-19   最后修改:2010-01-19
yiminghe 写道
给你提点建议,你自己实现的compress不是很好哦,
1. CompressedStream 没有 buffer ?直接都写入stream不太好
2.wrapper getWriter 没有考虑字符编码问题?,request.getCharacterEncoding可以用用
3.filter 异常处理下

try{
chain.doFilter(request, compressionResponse);
}
finally{ 
            compressionResponse.close(); 
}

恩,已经参考Tomcat examples里面的compressionFilters修改了。
翻了翻servlet api
public interface ServletResponseDefines an object to assist a servlet in sending a response to the client. The servlet container creates a ServletResponse object and passes it as an argument to the servlet's service method.

To send binary data in a MIME body response, use the ServletOutputStream returned by getOutputStream(). To send character data, use the PrintWriter object returned by getWriter(). To mix binary and text data, for example, to create a multipart response, use a ServletOutputStream and manage the character sections manually.

The charset for the MIME body response can be specified explicitly using the setCharacterEncoding(java.lang.String) and setContentType(java.lang.String) methods, or implicitly using the setLocale(java.util.Locale) method. Explicit specifications take precedence over implicit specifications. If no charset is specified, ISO-8859-1 will be used. The setCharacterEncoding, setContentType, or setLocale method must be called before getWriter and before committing the response for the character encoding to be used.

See the Internet RFCs such as RFC 2045 for more information on MIME. Protocols such as SMTP and HTTP define profiles of MIME, and those standards are still evolving.

看下来如果charset未定义,就采用ISO-8859-1  ?
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics