0

0

前端js和css的压缩合并之wro4j_html/css_WEB-ITnose

php中文网

php中文网

发布时间:2016-06-21 08:52:23

|

1262人浏览过

|

来源于php中文网

原创

me:注:

找了一圈,在java社区和node社区,就没有找到中意的解决方案,总是带有各种各样的毛病。

看来ror社区还是拥有最强悍的设计和构架能力的,也许得益于ruby的精神。

最牛牛的人有时候只要一两个就够了。

下面的文还是不错的,可惜wro4j的愚笨复杂设计啊。

立即学习前端免费学习笔记(深入)”;

from: 使用wro4j和maven在编译期间压缩js和css文件

最近在对一个web系统做性能优化. 

而对用到的静态资源文件的压缩整合则是前端性能优化中很重要的一环. 

好处不仅在于能够减小请求的文件体积,而且能够减少浏览器的http请求数. 

因为是基于java的web系统,并且使用的是nginx+tomcat做为服务器. 

最后考虑用wro4j和maven plugin在编译期间压缩静态资源. 

优化前: 

基本上所有的jsp都引用了这一大坨静态文件: 

                                

引用的文件很多,并且文件体积没有压缩,导致页面请求的时间非常长. 

另外还有一个问题,就是为了能够充分利用浏览器的缓存,静态资源的文件名称最好能够做到版本化控制. 

这样前端web服务器就可以放心大胆的开启缓存功能而不用担心缓存过期问题,因为如果一旦静态资源文件有修改的话, 

会重新生成一个文件名称. 

下面我根据自己项目的经验,来介绍下如何较好的解决这两个问题. 

分两步进行. 

第一步:引入wro4j,在编译时期将上述分散的多个文件整合成少数几个文件,并且将文件最小化. 

第二步:在生成的静态资源文件的文件名称上加入时间信息 

这是两步优化之后的引用情况: 

${platform:cssFile("/wro/basic") }    ${platform:jsFile("/wro/basic") }  ${platform:jsFile("/wro/custom") }  

只引用了1个css文件,2个js文件.http请求从10几个减少到3个,并且整体文件体积缩小了近一半. 

下面介绍优化流程. 

第一步:合并并且最小化文件. 

1.添加wro4j的maven依赖 

1.6.2       ...         ro.isdc.wro4j    wro4j-core    ${wro4j.version}                      org.slf4j      slf4j-api              

2.添加wro4j maven plugin 

         ro.isdc.wro4j      wro4j-maven-plugin      ${wro4j.version}                     compile                 run                                  basic,custom               true       ${basedir}/src/main/webapp/wro/       ${basedir}/src/main/webapp/           com.rootrip.platform.common.web.wro.CustomWroManagerFactory                   

如果开发环境是eclipse的话,可以下载m2e-wro4j这个插件. 

下载地址:http://download.jboss.org/jbosstools/updates/m2e-wro4j/ 

这个插件的主要功能是能够帮助我们在开发环境下修改对应的静态文件,或者pom.xml文件的时候能够自动生成打包好的js和css文件. 

对开发来说就会方便很多.只要修改源文件就能看见修改后的结果. 

3.在WEB-INF目录下添加wro.xml文件,这个文件的作用就是告诉wro4j需要以怎样的策略打包jss和css文件. 

           /css/basic.css    /css/skin.css    /css/jquery-ui-1.8.23.custom.css    /css/validationEngine.jquery.css        /js/jquery-1.7.2.min.js    /js/jquery-ui-1.8.23.custom.min.js    /js/jquery.validationEngine.js    /js/jquery.fixedtableheader.min.js    /js/roll.js    /js/jquery.pagination.js    /js/jquery.rooFixed.js    /js/jquery.ui.datepicker-zh-CN.js    /js/json2.js             /js/jquery.validationEngine-zh_CN.js    /js/common.js         

官方文档:http://code.google.com/p/wro4j/wiki/WroFileFormat 

其实这个配置文件很好理解,如果不愿看官方文档的朋友我在这简单介绍下. 

上面这样配置的目的就是告诉wro4j要将 

/css/basic.css 

/css/skin.css 

/css/jquery-ui-1.8.23.custom.css 

/css/validationEngine.jquery.css 

这四个文件整合到一起,生成一个叫basic.css的文件到指定目录(wro4j-maven-plugin里配置的),将 

/js/jquery-1.7.2.min.js 

/js/jquery-ui-1.8.23.custom.min.js 

/js/jquery.validationEngine.js 

/js/jquery.fixedtableheader.min.js 

/js/roll.js 

/js/jquery.pagination.js 

/js/jquery.rooFixed.js 

/js/jquery.ui.datepicker-zh-CN.js 

/js/json2.js 

这几个文件整合到一起,生成一个叫basic.js的文件到指定目录. 

知鹿匠
知鹿匠

知鹿匠教师AI工具,新课标教案_AI课件PPT_作业批改

下载

最后将 

/js/jquery.validationEngine-zh_CN.js 

/js/common.js 

这两个文件整合到一起,,生成一个叫custom.js的文件到指定目录. 

第一步搞定,这时候如果你的开发环境是eclipse并且安装了插件的话,应该就能在你工程的%your webapp%/wor/目录下看见生成好的 

basic.css,basic.js和custom.js这三个文件了. 

然后你再将你的静态资源引用路径改成 

      

就ok了.每次修改被引用到的css或js文件的时候,这些文件都将重新生成. 

如果开发环境是eclipse但是没有安装m2e-wro4j插件的话,pom.xml可能需要额外配置. 

请参考: https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j  

第二步:给生成的文件名称中加入时间信息并通过el自定义函数引用脚本文件. 

1. 创建DailyNamingStrategy类 

public class DailyNamingStrategy extends TimestampNamingStrategy {      protected final Logger log = LoggerFactory.getLogger(DailyNamingStrategy.class);     @Override   protected long getTimestamp() {    String dateStr = DateUtil.formatDate(new Date(), "yyyyMMddHH");    return Long.valueOf(dateStr);   }         }  

2.创建CustomWroManagerFactory类 

//这个类就是在wro4j-maven-plugin里配置的wroManagerFactory参数  public class CustomWroManagerFactory extends    DefaultStandaloneContextAwareManagerFactory {   public CustomWroManagerFactory() {    setNamingStrategy(new DailyNamingStrategy());   }  }  

上面这两个类的作用是使用wro4j提供的文件命名策略,这样生成的文件名就会带上时间信息了. 

例如:basic-2013020217.js 

但是现在又会发现一个问题:如果静态资源文件名称不固定的话,那怎么样引用呢? 

这时候就需要通过动态生成

因为项目使用的是jsp页面,所以通过el自定义函数来实现标签生成. 

3.创建PlatformFunction类 

public class PlatformFunction {      private static Logger log = LoggerFactory.getLogger(PlatformFunction.class);         private static ConcurrentMap staticFileCache = new ConcurrentHashMap<>();      private static AtomicBoolean initialized = new AtomicBoolean(false);      private static final String WRO_Path = "/wro/";      private static final String JS_SCRIPT = "";   private static final String CSS_SCRIPT = "";      private static String contextPath = null;       /**   * 该方法根据给出的路径,生成js脚本加载标签   * 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以js后缀结尾的文件名称名称.   * 然后拼成返回   * 如果查找到多个文件,返回根据文件名排序最大的文件   * @param str   * @return   */   public static String jsFile(String filePath) {    String jsFile = staticFileCache.get(buildCacheKey(filePath, "js"));    if(jsFile == null) {     log.error("加载js文件失败,缓存中找不到对应的文件[{}]", filePath);    }    return String.format(JS_SCRIPT, jsFile);   }      /**   * 该方法根据给出的路径,生成css脚本加载标签   * 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以css后缀结尾的文件名称名称.   * 然后拼成返回   * 如果查找到多个文件,返回根据文件名排序最大的文件   * @param str   * @return   */   public static String cssFile(String filePath) {    String cssFile = staticFileCache.get(buildCacheKey(filePath, "css"));    if(cssFile == null) {     log.error("加载css文件失败,缓存中找不到对应的文件[{}]", filePath);    }    return String.format(CSS_SCRIPT, cssFile);   }      public static void init() throws IOException {    if(initialized.compareAndSet(false, true)) {     ServletContext sc = Platform.getInstance().getServletContext();     if(sc == null) {      throw new PlatformException("查找静态资源的时候的时候发现servlet context 为null");     }     contextPath = Platform.getInstance().getContextPath();     File wroDirectory = new ServletContextResource(sc, WRO_Path).getFile();     if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {      throw new PlatformException("查找静态资源的时候发现对应目录不存在[" + wroDirectory.getAbsolutePath() + "]");     }     //将wro目录下已有文件加入缓存     for(File file : wroDirectory.listFiles()) {      handleNewFile(file);     }     //监控wro目录,如果有文件生成,则判断是否是较新的文件,是的话则把文件名加入缓存     new Thread(new WroFileWatcher(wroDirectory.getAbsolutePath())).start();    }   }     private static void handleNewFile(File file) {    String fileName = file.getName();    Pattern p = Pattern.compile("^(\\w+)\\-\\d+\\.(js|css)$");    Matcher m = p.matcher(fileName);    if(!m.find() || m.groupCount() < 2) return;    String fakeName = m.group(1);    String fileType = m.group(2);    //暂时限定只能匹配/wro/目录下的文件    String key = buildCacheKey(WRO_Path + fakeName, fileType);    if(staticFileCache.putIfAbsent(key, fileName) != null) {     synchronized(staticFileCache) {      String cachedFileName = staticFileCache.get(key);      if(fileName.compareTo(cachedFileName) > 0) {       staticFileCache.put(key, contextPath + WRO_Path + fileName);      }     }    }   }      private static String buildCacheKey(String fakeName, String fileType) {    return fakeName + "-" + fileType;   }      static class WroFileWatcher implements Runnable {        private static Logger log = LoggerFactory.getLogger(WroFileWatcher.class);        private String wroAbsolutePathStr;        public WroFileWatcher(String wroPathStr) {     this.wroAbsolutePathStr = wroPathStr;    }      @Override    public void run() {     Path path = Paths.get(wroAbsolutePathStr);     File wroDirectory = path.toFile();     if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {      String message = "监控wro目录的时候发现对应目录不存在[" + wroAbsolutePathStr + "]";      log.error(message);      throw new PlatformException(message);     }     log.warn("开始监控wro目录[{}]", wroAbsolutePathStr);     try {      WatchService watcher = FileSystems.getDefault().newWatchService();      path.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);            while (true) {       WatchKey key = null;       try {        key = watcher.take();       } catch (InterruptedException e) {        log.error("", e);        continue;       }       for (WatchEvent event : key.pollEvents()) {        if (event.kind() == StandardWatchEventKinds.OVERFLOW) {         continue;        }        WatchEvent e = (WatchEvent) event;        Path filePath = e.context();        handleNewFile(filePath.toFile());       }       if (!key.reset()) {        break;       }      }     } catch (IOException e) {      log.error("监控wro目录发生错误", e);     }     log.warn("停止监控wro目录[{}]", wroAbsolutePathStr);    }   }  }  

对应的tld文件就不给出了,根据方法签名编写就行了. 

其中的cssFile和jsFile方法分别实现引用css和js文件. 

在页面使用的时候类似这样: 

${platform:cssFile("/wro/basic") } 

${platform:jsFile("/wro/custom") } 

这个类的主要功能就是使用jdk7的WatchService监控wro目录的新增文件事件, 

一旦有新的文件加到目录里,判断这个文件是不是最新的,如果是的话则使用这个文件名称引用. 

这样一旦有新加的资源文件放到wro目录里,则能够自动被引用,不需要做任何代码上的修改,并且基本不影响性能. 

到此为止功能已经实现. 

但是我考虑到还有两个问题有待完善: 

1.因为生成的文件名称精确到小时,如果这个小时之内有多次代码修改,生成的文件名都完全一样. 

这样就算线上的代码有修改,对于已经有该文本缓存的浏览器来说,不会重新请求文件,也就看不到文件变化. 

不过一般来说线上代码不会如此频繁改动,对于大多数应用来说影响不大. 

2.在开发环境开发一段时间之后,wro目录下会生成一大堆的文件(因为m2e-wro4j插件在生成新的文件的时候不会删除旧文件,如果文件名相同会覆盖掉以前的文件), 

这时候就需要手动删除时间靠前的旧文件,虽然系统会忽略旧文件,但是我相信大多数程序员和我一样是有些许洁癖的吧. 

解决办法还是不少,比如可以写脚本定期清理掉旧文件. 

时间有限,有些地方考虑的不是很完善,欢迎拍砖. 

参考资料: 

http://meri-stuff.blogspot.sk/2012/08/wro4j-page-load-optimization-and-lessjs.html#Configuration  

https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j  

http://code.google.com/p/wro4j/wiki/MavenPlugin  

http://code.google.com/p/wro4j/wiki/WroFileFormat  

http://java.dzone.com/articles/using-java-7s-watchservice

+

+

+

-

+

+

+

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

9

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

12

2026.01.30

python 字符串格式化
python 字符串格式化

本专题整合了python字符串格式化教程、实践、方法、进阶等等相关内容,阅读专题下面的文章了解更多详细操作。

4

2026.01.30

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

20

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

18

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

19

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

3

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 极速入门
CSS3 极速入门

共36课时 | 8.4万人学习

千锋PHP-CSS入门及应用视频教程
千锋PHP-CSS入门及应用视频教程

共18课时 | 3.5万人学习

CSS视频教程-玉女心经版
CSS视频教程-玉女心经版

共25课时 | 39.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号