如何使用Grunt.js(0.3.x)合并和压缩多个CSS和JavaScript文件?

98
注意:这个问题仅适用于 Grunt 0.3.x,已经被保留作为参考。如需获取最新的 Grunt 1.x 版本,请参见我在此问题下面的评论。
我目前正在尝试使用 Grunt.js 来设置自动构建过程,首先要将 CSS 和 JavaScript 文件串联起来,然后将它们缩小。
我已经成功地将 JavaScript 文件串联和缩小了,但每次运行 grunt 时,似乎都是将文件附加到末尾,而不是覆盖原文件。
至于缩小甚至串联 CSS,迄今为止我还无法做到!
关于grunt CSS模块,我尝试使用consolidate-css、grunt-css和cssmin,但都没有成功。我无法理解如何使用它们!
我的目录结构如下(典型的node.js应用程序):
- app.js - grunt.js - /public/index.html - /public/css/[各种 css 文件] - /public/js/[各种 javascript 文件]
下面是我在应用程序根目录中的grunt.js文件的内容:
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    concat: {
      dist: {
        src: 'public/js/*.js',
        dest: 'public/js/concat.js'
      }
    },
    min: {
      dist: {
        src: 'public/js/concat.js',
        dest: 'public/js/concat.min.js'
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        node: true
      },
      globals: {
        exports: true,
        module: false
      }
    },
    uglify: {}
  });

  // Default task.
  grunt.registerTask('default', 'concat min');

};

总结一下,我需要两个问题的帮助:

  1. 如何将文件夹/public/css/中的所有CSS文件合并并压缩到一个文件中,比如main.min.css
  2. 为什么每次运行命令grunt时,Grunt.js会继续添加到/public/js/下的已合并和压缩的JavaScript文件concat.jsconcat.min.js而不是覆盖它们?

更新于2016年7月5日 - 从Grunt 0.3.x升级到Grunt 0.4.x或1.x

Grunt.jsGrunt 0.4.x中已经迁移到新的结构(现在称为Gruntfile.js)。请查看我的开源项目Grunt.js Skeleton以获取有关为Grunt 1.x设置构建流程的帮助。

Grunt 0.4.xGrunt 1.x 不应引入太多重大变更

5个回答

107

concat任务的源文件public/js/*.js中包含了concat.js。你可以创建一个任务,在重新连接之前删除concat.js(如果该文件存在),传递一个数组来明确定义要连接的文件及其顺序,或者更改项目结构。

如果选择更改结构,你可以把所有源文件放在./src下,把生成的文件放在./dest下。

src
├── css
│   ├── 1.css
│   ├── 2.css
│   └── 3.css
└── js
    ├── 1.js
    ├── 2.js
    └── 3.js

然后设置你的concat任务

concat: {
  js: {
    src: 'src/js/*.js',
    dest: 'dest/js/concat.js'
  },
  css: {
    src: 'src/css/*.css',
    dest: 'dest/css/concat.css'
  }
},

你的min任务

min: {
  js: {
    src: 'dest/js/concat.js',
    dest: 'dest/js/concat.min.js'
  }
},

内置的min任务使用了UglifyJS,因此您需要一个替代品。我发现grunt-css非常好用。安装后,将其加载到 grunt 文件中。

grunt.loadNpmTasks('grunt-css');

然后设置它

cssmin: {
  css:{
    src: 'dest/css/concat.css',
    dest: 'dest/css/concat.min.css'
  }
}

请注意,使用方式类似于内置的min函数。

将您的default任务更改为

grunt.registerTask('default', 'concat min cssmin');

现在,运行grunt将会产生你想要的结果。

dest
├── css
│   ├── concat.css
│   └── concat.min.css
└── js
    ├── concat.js
    └── concat.min.js

1
你真是个天才!我从来没有想到,如果我把concat放在同一个js文件夹里,它就会被捆绑并附加上去!我已经开始使用cssmin了,效果非常好!再次感谢。 - Jasdeep Khalsa
1
太好了,真的很棒!但文件名总是concat.min.cssconcat.min.js。如果我修改了一些东西,重新运行和部署,人们将无法获取最新版本,因为浏览器已经缓存了它。有没有一种方法可以随机命名文件并根据适当的<link><script>标签链接它们? - Tárcio Zemel
3
你可以在文件名中包含 package.json 的版本号:concat.min-<%= pkg.version %>.js - Rui Nunes
1
你可以像这样使用.destcssmin: { css:{ src: '<%= concat.css.dest %>', dest: './css/all.min.css' } } 这是操作concat -> css的结果。 - Ivan Black

12
我想在这里提到一种非常有趣的技术,它被用于像jQuery和Modernizr这样的大型项目中来连接各种东西。
这两个项目都完全使用requirejs模块进行开发(您可以在它们的github仓库中看到),然后它们使用requirejs优化器作为非常智能的连接器。有趣的是,正如您所见,不需要requirejs也可以使用jQuery和Modernizr,因为它们消除了requirejs的句法仪式,以便在其代码中摆脱requirejs。因此,他们最终得到了一个独立的库,该库是使用requirejs模块开发的!由于这一点,他们能够执行自定义构建操作等其他优势。
对于所有那些对使用requirejs优化器进行连接感兴趣的人,请查看此帖子:
这篇文章
此外,还有一个小工具可以抽象出整个过程的样板: AlbanilJS

这非常有趣,但与问题无关,因为RequireJS是一个模块加载器,据我所知它不会连接或压缩。 - Jasdeep Khalsa
谢谢!你说得对,RequireJS是一个模块加载器,它不会合并或压缩代码。但是r.js(RequireJS优化器-->http://requirejs.org/docs/optimization.html)可以做到。更具体地说,它将您的模块按依赖关系进行排序并将它们合并在一起。Modernizr和jQuery的开发人员利用了这个工具的排序算法,并将它作为智能合并工具使用。您可以查看他们的代码库以了解他们正在做什么。 - Augusto Altman Quaranta
使用grunt的concat或uglify,生成的文件是根据配置的指定源文件顺序组成的。而RequireJS基于依赖关系。虽然方法不同,但可以实现相同的结果。 - priyabagus

10

我同意上面的回答。但是这里有另一种CSS压缩的方法。

你可以使用YUI压缩器将你的CSS连接起来:

module.exports = function(grunt) {
  var exec = require('child_process').exec;
   grunt.registerTask('cssmin', function() {
    var cmd = 'java -jar -Xss2048k '
      + __dirname + '/../yuicompressor-2.4.7.jar --type css '
      + grunt.template.process('/css/style.css') + ' -o '
      + grunt.template.process('/css/style.min.css')
    exec(cmd, function(err, stdout, stderr) {
      if(err) throw err;
    });
  });
}; 

如果您正在使用“grunt-css”,则必须安装Locale Npm模块,因此请确保在安装“grunt-css”时已经安装了Locale Npm模块。 - Anshul
7
为什么要在构建过程中增加这么多复杂性并调用JVM?这只会没有理由地减慢整个过程。 - michael salmon

3
您不需要添加concat包,可以通过cssmin这样做:
cssmin : {
      options: {
            keepSpecialComments: 0
      },
      minify : {
            expand : true,
            cwd : '/library/css',
            src : ['*.css', '!*.min.css'],
            dest : '/library/css',
            ext : '.min.css'
      },
      combine : {
        files: {
            '/library/css/app.combined.min.css': ['/library/css/main.min.css', '/library/css/font-awesome.min.css']
        }
      }
    }

对于js,可以像这样使用uglify:

uglify: {
      my_target: {
        files: {
            '/library/js/app.combined.min.js' : ['/app.js', '/controllers/*.js']
        }
      }
    }

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接