现状
一次在公司的旧项目中的JavaScript
使用了es6
的语法如let时,打包代码出现了问题:
[ERROR] ...\src\main\webapp\pages\modules\main\order.js:line 81:column 16:missing ; before statement
let formObj = this.getForm();
[ERROR] ...\src\main\webapp\pages\modules\main\order.js:line 1:column 0:Compilation produced 1 syntax errors.
[ERROR] Failed to execute goal net.alchim31.maven:yuicompressor-maven-plugin:1.3.0:compress (default) on project ydt-web: Execution default of goal net.alchim31.maven:yuicompressor-maven-plugin:1.3.0:compress failed: Compilation produced 1 syntax errors. -> [Help 1]
问题分析
YUI Compressor
(下文简称为YUI
)不支持es6语法,且已经很长时间不更新了
- 其
Maven
插件yuicompressor-maven-plugin
也已经在2022年进入归档状态,不再维护,因为该插件的维护者都认为YUI
已经不适用于主流的JavaScript
开发了
- 而我们的项目也因为每次有新员工修改它时都会遇到语法的这个问题,所以我们想彻底解决它。
需求
- 线上的
JavaScript
和CSS
文件需要压缩减小体积,提升网页打开的效率 - 支持
es6
语法,降低开发复杂度 - 只在打包时压缩代码,开发时的源代码不压缩,避免代码混乱难以维护
- 使用的插件最好近期有更新维护,避免遇到问题无法获得技术支持
方案搜寻
- 在
YUI
的issue
中找到了另一个支持压缩es6
语法的插件minify-maven-plugin
,但这个插件我使用的时候出现了一个问题,写了如下的代码会报错:
// 花括号的最后一个元素不能有逗号
var a = {
test1: 1,
test2: 2,
}
虽然后面也在
issue
中找到了解决方法(设置closureLanguage),但发现这个项目已经很久没再维护,使用手册也已经404
了,因此这个插件也被pass掉
- 再之后看到
resources-optimizer-maven-plugin
的作者自荐,发现这个插件满足我们上面的需求,而且表明修复了YUI
的问题,就决定采用这个插件
下面是添加resources-optimizer-maven-plugin
插件依赖的代码:
<plugin>
<groupId>org.primefaces.extensions</groupId>
<artifactId>resources-optimizer-maven-plugin</artifactId>
<!-- 目前支持jdk8的最新版本 -->
<version>2.4.4</version>
</plugin>
项目在打包过程中看到控制台输出了以下内容说明成功引入:
完成后会显示压缩情况,在我们的项目中的JavaScript
和CSS
压缩了50%+
实现过程的踩坑
- 使用
resources-optimizer-maven-plugin
后源代码被压缩- 在maven的生命周期
maven-resources-plugin
中进行操作 - 修改压缩代码的来源目录
- 在maven的生命周期
- 打包成war包后,里面的
JavaScript
却是未压缩的源码JavaScript
文件在压缩之后,到maven的生成war包环节时会重新将JavaScript
和CSS
等内容复制,因此我们需要在maven-war-plugin
中配置不复制JavaScript
和CSS
文件(见下文代码实现的构建期间<build>
配置)
代码实现
在进行了一圈的搜罗后,使用了resources-optimizer-maven-plugin
插件,并在pom.xml
文件中加入如下配置:
压缩插件引入及配置
<plugin>
<groupId>org.primefaces.extensions</groupId>
<artifactId>resources-optimizer-maven-plugin</artifactId>
<!-- 支持jdk8下的最新版本,再往上的版本就不支持jdk8了 -->
<version>2.4.4</version>
<executions>
<execution>
<id>optimize</id>
<!-- 只在准备打包时执行 -->
<phase>prepare-package</phase>
<goals>
<goal>optimize</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- 是否不执行压缩 -->
<skip>false</skip>
<!-- 压缩级别 -->
<compilationLevel>SIMPLE_OPTIMIZATIONS</compilationLevel>
<!-- js规范版本 -->
<languageIn>ECMASCRIPT_2015</languageIn>
<resourcesSets>
<resourcesSet>
<!-- js文件所在的目录,如果不配置的话会将全部js进行压缩 -->
<inputDir>${project.build.directory}/${package.name}</inputDir>
<includes>
<!-- 需要压缩的文件,支持通配符 -->
<include>**/*.js</include>
<include>**/*.css</include>
</includes>
<excludes>
<!-- 不压缩的文件,支持通配符 -->
<exclude>**/*.min.js</exclude>
</excludes>
</resourcesSet>
</resourcesSets>
</configuration>
</plugin>
构建期间配置
<build>
<plugins>
<!-- 打为war包时 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!--
如果不增加此配置 src/main/webapp 下面的内容 会重新复制到target输出目录 覆盖掉编译后的内容
这样编译的还是未压缩过的内容,增加上此过滤 打war包就不会内容覆盖-->
<warSourceExcludes>**/*.js, **/*.css</warSourceExcludes>
</configuration>
</plugin>
<!-- 处理静态资源目录时 -->
<plugin>
<!-- 在maven的构建阶段把js文件复制到target目录下 -->
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-js-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${package.name}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/webapp</directory>
<filtering>true</filtering>
<includes>
<include>**/*.js</include>
<include>**/*.css</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
总结
使用resources-optimizer-maven-plugin
插件的优势如下:
- 更多的压缩方式:支持
Google Closure Compiler
的JavaScript
编译压缩方式和YUI
对CSS
的压缩方式 - 对于新语法有更好的支持:
Google Closure Compiler
对于es6支持良好,其JavaScript
的压缩效果也比YUI
更好 - 更好的技术支持:此插件最近一次维护时间是2024年10月1日,目前所有
issue
已经是解决状态,反观minify-maven-plugin
和yuicompressor-maven-plugin
都已经多年未维护,所以推荐有需要的可以看看这个新插件
带来的问题思考:
Google Closure Compiler
对于JavaScript
的编写会更严格,当存在不规范的写法时会报错,如同一个方法名在一个JavaScript
文件中出现两次- 前后端分离的项目应该用不上这个插件,现在的
vue
项目都有webpack
之类的自动压缩转换JavaScript
的工具,因此这个插件可能更多用于老项目上