Como concatenar e reduzir vários arquivos CSS e JavaScript com Grunt.js (0.3.x)

99

Nota: Esta questão é relevante apenas para Grunt 0.3.xe foi deixada para referência. Para obter ajuda com a versão mais recente do Grunt 1.x, consulte meu comentário abaixo desta pergunta.

Atualmente, estou tentando usar Grunt.js para configurar um processo de compilação automático para primeiro concatenar e, em seguida, minimizar arquivos CSS e JavaScript.

Consegui concatenar e minificar com sucesso meus arquivos JavaScript, embora cada vez que executo o grunt, ele pareça apenas anexar ao arquivo em vez de sobrescrevê-los.

Quanto à minimização ou mesmo concatenação de CSS, ainda não consegui fazer isso!

Em termos de módulos CSS grunhido Eu tentei usar consolidate-css, grunt-css& cssmin, mas sem sucesso. Não consegui entender como usá-los!

Minha estrutura de diretório é a seguinte (sendo um aplicativo node.js típico):

  • app.js
  • grunt.js
  • /public/index.html
  • / public / css / [vários arquivos css]
  • / public / js / [vários arquivos javascript]

Aqui está a aparência do meu arquivo grunt.js atualmente na pasta raiz do meu aplicativo:

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');

};

Para resumir, preciso de ajuda com duas perguntas:

  1. Como concatenar e reduzir todos os meus arquivos css na pasta /public/css/em um arquivo, por exemplomain.min.css
  2. Por que grunt.js continua anexando aos arquivos javascript concatenados e minificados concat.jse concat.min.jsem /public/js/vez de substituí-los sempre que o comando grunté executado?

Atualizado em 5 de julho de 2016 - Atualização de Grunt 0.3.x para Grunt 0.4.x ou 1.x

Grunt.jsfoi movido para uma nova estrutura em Grunt 0.4.x(o arquivo agora é chamado Gruntfile.js). Consulte meu projeto de código aberto Grunt.js Skeleton para obter ajuda na configuração de um processo de construção para Grunt 1.x.

Mudar de Grunt 0.4.xpara Grunt 1.x não deve apresentar muitas mudanças importantes .

Jasdeep Khalsa
fonte

Respostas:

107

concat.js está sendo incluído nos concatarquivos de origem da tarefa public/js/*.js. Você pode ter uma tarefa que remove concat.js(se o arquivo existir) antes de concatenar novamente, passar uma matriz para definir explicitamente quais arquivos você deseja concatenar e sua ordem, ou alterar a estrutura do seu projeto.

Se fizer o último, você pode colocar todas as suas fontes em ./srce seus arquivos construídos em./dest

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

Em seguida, configure sua tarefa concat

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

Sua tarefa mínima

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

A tarefa min integrada usa UglifyJS, portanto, você precisa de uma substituição. Achei o grunt-css muito bom. Depois de instalá-lo, carregue-o em seu arquivo grunt

grunt.loadNpmTasks('grunt-css');

E então configure

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

Observe que o uso é semelhante ao mínimo embutido.

Mude sua defaulttarefa para

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

Agora, a execução gruntproduzirá os resultados desejados.

dest
├── css
   ├── concat.css
   └── concat.min.css
└── js
    ├── concat.js
    └── concat.min.js
jaime
fonte
1
Você é um gênio! Nunca me ocorreu que, claro, se eu incluísse concatna mesma jspasta, isso iria pegá-la e anexá-la! Comecei a usar o cssmin e funciona muito bem! Obrigado novamente.
Jasdeep Khalsa
1
Ótimo, ótimo mesmo! Mas os nomes dos arquivos são sempre concat.min.csse concat.min.js. Se eu modificar algo, executar novamente e implantar, as pessoas não obterão a versão mais recente porque o navegador já a armazenou em cache. Existe alguma maneira de nomear aleatoriamente o arquivo e vinculá-los de acordo com as tags <link>e adequadas <script>?
Tárcio Zemel
3
@ TárcioZemel Você pode incluir a versão do package.json no nome do arquivo:concat.min-<%= pkg.version %>.js
Rui Nunes
1
Você pode usar .destassim cssmin: { css:{ src: '<%= concat.css.dest %>', dest: './css/all.min.css'}} `É o resultado da operaçãoconcat -> css
Ivan Black
12

Quero mencionar aqui uma técnica muito, MUITO interessante que está sendo usada em projetos enormes como jQuery e Modernizr para concatenar coisas.

Ambos os projetos são inteiramente desenvolvidos com módulos requirejs (você pode ver isso em seus repositórios github) e então eles usam o otimizador requirejs como um concatenador muito inteligente. O interessante é que, como você pode ver, nem o jQuery nem o Modernizr precisa do requirejs para funcionar, e isso acontece porque eles apagam o ritual sintático do requirejs para se livrar dos requirejs do seu código. Então, eles acabam com uma biblioteca autônoma que foi desenvolvida com módulos requirejs! Graças a isso, eles são capazes de realizar compilações cutom de suas bibliotecas, entre outras vantagens.

Para todos aqueles interessados ​​em concatenação com o otimizador requirejs, confira esta postagem

Também existe uma pequena ferramenta que abstrai todo o boilerplate do processo: AlbanilJS

Augusto Altman Quaranta
fonte
Isso é muito interessante, mas não relevante para a questão, uma vez que RequireJS é um carregador de módulo, ele não concatena ou
minimiza
Obrigado! Você está certo, RequireJS é um carregador de módulo e não concatena nem diminui. Mas r.js (o otimizador RequireJS -> requirejs.org/docs/optimization.html ) faz isso. Mais especificamente, ele concatena seus módulos ordenando-os por dependências. Os caras da Modernizr e jQuery aproveitaram o algoritmo de ordenação da ferramenta e o utilizaram especificamente como um concatenador inteligente. Você pode verificar seus repositórios para ver o que estão fazendo.
Augusto Altman Quaranta
Usando concat ou uglify do grunt, o arquivo resultante é composto usando a ordem dos arquivos de origem especificados configurados. Enquanto RequireJS é baseado na dependência. Uma abordagem diferente ainda pode alcançar o mesmo resultado.
priyabagus
10

Eu concordo com a resposta acima. Mas aqui está outra forma de compressão CSS.

Você pode concatenar seu CSS usando o compressor YUI :

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;
    });
  });
}; 
Anshul
fonte
Caso você esteja usando 'grunt-css', você deve precisar do módulo Locale Npm, portanto, certifique-se de que ao instalar o 'grunt-css' deve estar no módulo Locale Npm.
Anshul
7
Por que você deseja fazer toda essa complexidade e chamar um jvm em seu processo de construção? Apenas retarda tudo sem motivo.
salmão michael
3

Você não precisa adicionar o pacote concat, você pode fazer isso via cssmin desta forma:

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']
        }
      }
    }

E para js, use uglify assim:

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