YUI3中,为了不js文件过大,各个功能模块是拆分的。它有一个“种子”的概念:先下载一个小的核心的js文件到浏览器端,再经过这个小的js文件去加载其它所需的模块。 css
这种按需加载虽然解决了单个js过大的问题,可是随之带来另一个问题:若是一个页面使用了YUI的a、b、c功能,那么浏览器就要向服务器请求a.js、b.js、c.js三个文件,这样增长了浏览器向服务器的沟通次数。 java
为了解决后面的问题,YUI3又有一个combine的概念,预先下载的那个核心的js,把页面上须要的a、b、c模块合并成一个请求发给服务器,相似这样:http://mydomain.com/conbine?a.js&b.js&c.js。 web
这要求服务器接收到http://mydomain.com/conbine请求后,将参数取出来,找到对应的a、b、c的js文件,合并成一个js文件,返回给客户端。Combo Handler是Yahoo!开发的一个Apache模块,专门来干这个事情的。 apache
若是只用java的web容器,能够把这项工做委托给servlet: 浏览器
<servlet> <servlet-name>yuicombo</servlet-name> <servlet-class>org.siqisource.mozo.servlets.YuiCombineServlet</servlet-class> </servlet>对应的YUI配置为:
YUI.GlobalConfig = { combine: true, comboBase: '<mz:webRoot/>/yuicombo?', root: 'uilibrary/yui/', };
在servlet代码中,我使用了YUI的yuicompressor来压缩js和css文件,下面是maven配置。 缓存
<dependency> <groupId>com.yahoo.platform.yui</groupId> <artifactId>yuicompressor</artifactId> <version>2.4.7</version> </dependency>
其中的Path类只是为了得到web应用物理路径,在是用的时候替换一下便可。 服务器
目前已知的缺陷:对于css的按需加载,浏览器会请求客户端两次,目前不清楚是否是YUI3(测试版本:3.7.2)的问题。 app
具体代码以下: dom
package org.siqisource.mozo.servlets; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FileDeleteStrategy; import org.apache.commons.io.FileUtils; import org.siqisource.mozo.context.Path; import com.yahoo.platform.yui.compressor.CssCompressor; import com.yahoo.platform.yui.compressor.JavaScriptCompressor; public class YuiCombineServlet extends HttpServlet { private static Map<String, String> cachedResources = new HashMap<String, String>(); private String cacheContextPath = "uilibrary/yui/cache/"; private String cacheDir = Path.getPhysicalPath() + cacheContextPath; int linebreakpos = -1; boolean munge = true; boolean verbose = false; boolean preserveAllSemiColons = false; boolean disableOptimizations = false; @Override public void init() throws ServletException { // 重置缓存文件夹 File catchedDir = new File(cacheDir); if (catchedDir.exists()) { FileDeleteStrategy strategy = FileDeleteStrategy.FORCE; try { strategy.delete(catchedDir); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catchedDir.mkdirs(); super.init(); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String queryString = request.getQueryString(); String resourcePath = cachedResources.get(queryString); // 已缓存 if (resourcePath == null) { String[] resources = queryString.split("&"); String firstResource = resources[0]; String fileName = UUID.randomUUID().toString(); if (firstResource.endsWith(".js")) { fileName += ".js"; Writer writer = new FileWriter(cacheDir + fileName); for (String resource : resources) { Reader reader = new FileReader(Path.getPhysicalPath() + resource); JavaScriptCompressor compressor = new JavaScriptCompressor( reader, null); compressor.compress(writer, linebreakpos, munge, verbose, preserveAllSemiColons, disableOptimizations); reader.close(); } writer.flush(); writer.close(); } else if (resources[0].endsWith(".css")) { fileName += ".css"; Writer writer = new FileWriter(cacheDir + fileName); for (String resource : resources) { Reader reader = new FileReader(replacedUrlFile(resource)); CssCompressor compressor = new CssCompressor(reader); compressor.compress(writer, linebreakpos); reader.close(); } writer.flush(); writer.close(); } resourcePath = cacheContextPath + fileName; cachedResources.put(queryString, resourcePath); } request.getRequestDispatcher(resourcePath).forward(request, response); return; } public String replacedUrlFile(String fileName) throws IOException { String cssfilePath = Path.getPhysicalPath() + fileName; File cssFile = new File(cssfilePath); String tempCssFilePath = cacheDir + "tmp-css-" + cssFile.getName(); File tempCssFile = new File(tempCssFilePath); if (tempCssFile.exists()) { return tempCssFilePath; } // 判断是否须要替换 String css = FileUtils.readFileToString(cssFile); int maxIndex = css.length() - 1; int appendIndex = 0; Pattern p = Pattern.compile("url\\(\\s*([\"']?)"); if (!p.matcher(css).find()) { return cssfilePath; } // 真的须要替换 Matcher m = p.matcher(css); String url = fileName.substring(0, fileName.lastIndexOf('/')); url = Path.getContextPath() + "/" + url + "/"; StringBuffer replacedUrlCss = new StringBuffer(); while (m.find()) { int startIndex = m.start() + 4; // "url(".length() String terminator = m.group(1); // ', " or empty (not quoted) if (terminator.length() == 0) { terminator = ")"; } boolean foundTerminator = false; int endIndex = m.end() - 1; while (foundTerminator == false && endIndex + 1 <= maxIndex) { endIndex = css.indexOf(terminator, endIndex + 1); if ((endIndex > 0) && (css.charAt(endIndex - 1) != '\\')) { foundTerminator = true; if (!")".equals(terminator)) { endIndex = css.indexOf(")", endIndex); } } } // Enough searching, start moving stuff over to the buffer replacedUrlCss.append(css.substring(appendIndex, m.start())); if (foundTerminator) { String token = css.substring(startIndex, endIndex); token = token.replaceAll("\\s+", ""); String preserver = "url('" + url + token + "')"; replacedUrlCss.append(preserver); appendIndex = endIndex + 1; } else { // No end terminator found, re-add the whole match. Should we // throw/warn here? replacedUrlCss.append(css.substring(m.start(), m.end())); appendIndex = m.end(); } } FileUtils.writeStringToFile(tempCssFile, replacedUrlCss.toString()); return tempCssFilePath; } }