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

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

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

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

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

优化前:

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

Html代码  收藏代码

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

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

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

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

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

分两步进行.

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

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

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

Html代码  收藏代码

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

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

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

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

下面介绍优化流程.

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

1.添加wro4j的maven依赖

Xml代码  收藏代码

1.6.2

...

ro.isdc.wro4j

wro4j-core

${wro4j.version}

org.slf4j

slf4j-api

2.添加wro4j maven plugin

Xml代码  收藏代码

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文件.

Java代码  收藏代码

xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd">

/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的文件到指定目录.

最后将

/js/jquery.validationEngine-zh_CN.js

/js/common.js

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

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

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

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

Html代码  收藏代码

就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类

Java代码  收藏代码

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类

Java代码  收藏代码

//这个类就是在wro4j-maven-plugin里配置的wroManagerFactory参数

public class CustomWroManagerFactory extends

DefaultStandaloneContextAwareManagerFactory {

public CustomWroManagerFactory() {

setNamingStrategy(new DailyNamingStrategy());

}

}

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

例如:basic-2013020217.js

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

这时候就需要通过动态生成与来解决了.

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

3.创建PlatformFunction类

Java代码  收藏代码

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

maven js css 压缩,使用wro4j和maven在编译期间压缩js和css文件(经典)相关推荐

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

    最近在对一个web系统做性能优化. 而对用到的静态资源文件的压缩整合则是前端性能优化中很重要的一环. 好处不仅在于能够减小请求的文件体积,而且能够减少浏览器的http请求数. 因为是基于java的we ...

  2. wro4j和maven plugin在编译期间压缩静态资源

    优化前:  基本上所有的jsp都引用了这一大坨静态文件: <link rel="stylesheet" type="text/css" href=&quo ...

  3. wro4j和maven plugin在编译期间压缩静态资源.

    优化前: 基本上所有的jsp都引用了这一大坨静态文件: <link rel="stylesheet" type="text/css" href=" ...

  4. 1.Maven+SpringMVC+Eclipse软件安装配置,Maven报插件错误,Eclipse总是卡死的解决办法,导入一个maven工程后 一直显示importing maven project

     使用Maven+SpringMVC+Eclipse软件安装配置过程中的问题: 1.Eclipse总是卡死的解决办法: 一:内存不足所以会卡死,配置一下eclipse.ini修改这几个值就好了-X ...

  5. js控制css 加载,CSS样式表的加载对于DOM解析,渲染,JS执行的阻塞问题

    CSS加载问题 css加载会不会阻塞DOM树的解析? css加载会不会阻塞DOM树的渲染? css加载会不会阻塞后面js语句的执行? css阻塞 h1 { color: red !important ...

  6. Maven第3篇:详解maven解决依赖问题

    Maven第3篇:详解maven解决依赖问题 本文主要内容 感受一下maven的效果 maven约定配置 maven中pom文件 maven坐标详解 maven依赖导入功能 maven依赖范围详解 m ...

  7. Maven(一):什么是Maven?

    Maven(一):什么是Maven? 前言 一.为什么要学习Maven? 1.Maven 作为依赖管理工具 1.1 jar 包的规模 1.2 jar 包的来源 1.3 jar 包之间的依赖关系 2.M ...

  8. Maven学习总结(七)——eclipse中使用Maven创建Web项目

    2019独角兽企业重金招聘Python工程师标准>>> Maven学习总结(七)--eclipse中使用Maven创建Web项目 一.创建Web项目 1.1 选择建立Maven Pr ...

  9. Maven实战(三)Eclipse构建Maven项目

    2019独角兽企业重金招聘Python工程师标准>>> 1. 安装m2eclipse插件     要用Eclipse构建Maven项目,我们需要先安装meeclipse插件     ...

最新文章

  1. Docker最全教程——从理论到实战(六)
  2. Quartz实现定时任务实例
  3. Linux中查询显卡硬件的几种命令(记录)
  4. linux 列表看多个文件数量,查看linux默认能最多开启多少个文件数量
  5. WebStrom的学习使用 H5开发
  6. 09: xmltodict 模块将xml格式转成json格式
  7. 免费好用的划词搜题神器插件
  8. JPG怎么批量转换成PDF
  9. FPGA中的AXI总线知识点快速学习(适合新手)
  10. 给PDF文档添加图片签名的简单方法
  11. 使用yum下载rpm源码包
  12. [CSAPP]Architecture Lab实验笔记
  13. 尚元钓(Fishing Game)
  14. 基于spec评论作品
  15. 地狱模式的居转户记录05
  16. Oracle.DataAccess.dll 下载 dll之家
  17. PTA-团体程序设计天梯赛-3
  18. 2020牛客暑期多校训练营(第五场)题解
  19. 都2019了,这些曾经的经典游戏还能在mac玩到吗?(第一弹仙剑客栈mac版)
  20. 有约束的最小二乘方图像复原方法_具有一阶约束的最小二乘方图像复原法

热门文章

  1. linux下freerdp编译,linux下安装freerdp连接windows远程桌面的好软件软件
  2. java新手面试微信交流群
  3. 1、安装Vmware
  4. CFS调度算法调度时机的理解
  5. 计算机组成原理时序与启停实验,计算机组成原理时序与启停实验.doc
  6. 实战|使用python推送微信公众号消息(哄女友专用)
  7. iPhone4升级iOS7输入卡顿的完美解决办法
  8. 【系统架构设计师】软考高级职称,一次通过,倾尽所有,看完这篇就够了,方法和技巧这里全都有。
  9. 梅州放射性实验室建设布局要求分析
  10. Oracle中对数据分组统计