分享 Grunt 前端自动化构建工具 [方便懒得看英文文档的新同学]

dandananddada · May 06, 2015 · Last by nightire replied at May 08, 2015 · 4570 hits

Grunt 是一个 JavaScript 构建工具。

安装

在安装 Grunt 前需要安装 node NPM。 首先全局安装 grunt-cli(grunt-cli 为 grunt 的命令工具)。 $ npm install -g grunt-cli 然后在开发的项目目录下安装 grunt。 $ npm install grunt 通过"—version"命令查看当前 cli 和 grunt 版本 $ grunt —version 通过 git 安装模板插件 $ git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntplugin 通过 grunt-init --help 命令可以查看到 Available templates,当前安装的模板名称为 gruntplugin。 安装 grunt 常用的插件。 $ npm install grunt-contrib-uglify $ npm install grunt-contrib-concat $ npm install grunt-contrib-watch 以上基本需要安装的东西都完成了,接下来进行配置。 当然这里你也可以不单独安装需要的 plugin,而是通过 npm install 命令直接安装 package.json 文件中引入的全部 plugins。

配置

Grunt 项目需要 package.json 和 Gruntfile.js/Gruntfile.coffee 这两个配置文件。其中 package.json 定义了项目中需要引入的 plugins,Gruntfile.js 定义了 Grunt 的配置信息。 首先通过 grunt-init [模板名] 创建 Gruntfile.js 文件和 package.json 文件。 $ grunt-init gruntplugin 接下来通过 npm init 命令根据提示输入项目相关相应参数,完善 package.json 文件。 $ npm init 这里也可以 clone 我使用的项目 $ git clone https://github.com/dandananddada/helloGrunt.git 然后在根目录下执行 $ npm install 接下来就可以通过 Grunt 的命令查看定义的 Task,并且执行相应任务了。 具体任务相关数据配置的说明可参考官网,不在这里细说了。 http://gruntjs.com/configuring-tasks

参考个人项目配置

https://github.com/dandananddada/helloGrunt.git

附录

Gruntfile.js

module.exports = function(grunt) {

    var config = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: config,
        //sass->css编译插件
        sass: {
            options: {
                sourceMap: false
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/styles/sass',
                    src: ['*.scss'],
                    dest: 'app/styles/',
                    ext: '.css'
                }]
            }
        },
        //清除build后生成的文件(清空dist目录)
        clean: {
            dist: {
                files: [{
                      dot: true,
                      src: [
                          '.tmp',
                          '<%= config.dist %>/*',
                          '!<%= config.dist %>/.git*'
                      ]
                }]
            },
        },
        //合并js和css文件
        concat: {
            js: {
                src: ['<%= config.app %>/js/**/*.js'],
                dest: '<%= config.dist %>/combine/<%= pkg.name %>.js'
            },
            css: {
                src: ['<%= config.app %>/styles/**/*.css'],
                dest: '<%= config.dist %>/combine/<%= pkg.name %>.css'  
            }
        },
        // 压缩css文件
        cssmin: {
            // 保持目录结构压缩
            compress: {
                files: [{
                      expand: true,
                      cwd: '<%= config.app %>/styles/',
                      src: ['*.css', '!*.min.css'],
                      dest: '<%= config.dist %>/release/css/',
                      ext: '.min.css'
                    }]
            },
            // 合并为一个文件后压缩
            build: {
                files: [{
                      expand: true,
                      cwd: '<%= config.dist %>/combine/',
                      src: ['*.css', '!*.min.css'],
                      dest: '<%= config.dist %>/release',
                      ext: '.min.css'
                    }]
            }
        },
        // 压缩js文件
        uglify: {
            options: {
            banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            // 保持目录结构压缩
            compress: {
                options: {
                    mangle: true,
                    compress: {
                        drop_console: true
                    },
                    report: "min"
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/js',
                    src: '**/*.js',
                    dest: '<%= config.dist %>/release/js',
                    ext: '.min.js'
                }]
            },
            // 合并为一个文件后压缩
            build: {
                options: {
                    mangle: true,
                    compress: {
                        drop_console: true
                    },
                    report: "min"
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dist %>/combine',
                    src: '**/*.js',
                    dest: '<%= config.dist %>/release',
                    ext: '.min.js'
                }]
            }
        },
        // 配置ctrl+s触发任务
        watch: {
            compile: {
                files: ['<%= config.app %>/styles/sass/**/*.scss'],
                tasks: ['sass'],
            },
            livereload: {
                options: {
                    livereload: '<%= connect.options.livereload %>'
                },
                files: [
                    '<%= config.app %>/{,*/}*.html',
                    '<%= config.app %>/{,*/}*.css',
                    '<%= config.app %>/images/{,*/}*'
                ]
            }
        },
        // 将当前站点作为服务器启动
        connect: {
            options: {
                port: 9001,
                open: true,
                livereload: 35729,
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function(connect) {
                        return [
                            connect.static(config.app)
                        ];
                    }
                }
            }
        },
        // 针对测试作为服务启动
        karma: {
            unit: {
                configFile: 'karma.conf.js'
            }
        }
    });

    grunt.loadNpmTasks('grunt-sass');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-karma');

    // 将sass文件编译为css文件
    grunt.registerTask('compile', ['sass']);
    // 清理dist目录
    // grunt clean,grunt-contrib-clean内置任务
    // 分别合并js和css文件为一个文件
    grunt.registerTask('combine',['compile','concat:css','concat:js']);
    // 保持原目录结构压缩css文件和js文件
    grunt.registerTask('compress',['compile','cssmin:compress','uglify:compress']);
    // 部署js文件和css文件(分别将js和css文件合并压缩为一个文件)
    grunt.registerTask('build',['clean:dist','compile','combine','cssmin:build','uglify:build']);
    //将当前站点作为服务启动
    grunt.registerTask('server', ['connect:livereload','watch','watch:compile']);
    // 启动js单元测试服务
    grunt.registerTask('test', ['karma']);
    // 设置grunt默认任务为server
    grunt.registerTask('default', ['server']);
};

gulp,不谢。

gulp+1,感觉写起来比这个简洁多了

正在往 grunt 转 gulp 飘过…… gulp 确实省下不少样板代码。

同弃用 grunt 转 gulp 路过。

其实 rake 和 thor 更好用...

#5 楼 @luikore grunt 有的功能,rake 都有么?

rake +1, thor +1 简单易用。

#6 楼 @lips 都有,而且不需要记那么复杂的 json 配置结构

make: 儿子们,你们爸爸回来了

不错

另外,新手强烈推荐 yeoman 这个组合套装

推荐一个 PPT,很有料,不过看完后估计都转 Gulp 了。Build Wars: Gulp V.S. Grunt

#8 楼 @luikore 我觉得生态系统不同,在 Rake 里面直接用各种 Gems 比较方便,但是 Grunt/Gulp 有庞杂的 Node 各种插件支持,比如 Orchestrator, stream, vinyl 什么的,这些在 Rake 里面弄一套好像没有见到有弄的不错的。

#11 楼 @lgn21st 有没有 rake vs gulp? 你在 ralls 项目中用 gulp?

#13 楼 @lips 不好意思,没有 rake vs gulp,如果是新项目,我会首选 gulp,这个东西没有那么复杂,网上学习资料非常丰富,不用花太多时间就能掌握。

#10 楼 @leopku 恰恰相反,我觉得越是新手越不应该推荐 yeoman,有经验的想要快速上手的才值得推荐一下 yeoman

#14 楼 @lgn21st 在 rails 项目中使用 gulp,这说明 rake 不行了么?

#16 楼 @lips gulp 跟 rake 擅长的事情不同。

#15 楼 @nightire 我这边实际推广的效果还不错的说

#18 楼 @leopku 唔,我值得并非推广的效果,而是初学者一旦依赖 yeoman,便很难再去深入学习一些东西,因为 yeoman 把它们“黑盒化”了。当然实际中还是要看个人的主观能动性的,只不过多数人不具备这种特质罢了。

前段时间招聘的时候有遇到这样的童鞋,yeoman 玩得很溜,也会写自定义的 generators,但是让他维护一下我们内部项目的一些构建流程就抓瞎了,因为我们用 gulp 的,而且用的很深,写了许多专为复杂构建场景用的 modules,他看 gulp 脚本之后的反应就是几乎看不懂在干吗……我跟他解释了几个过程,他立刻就说:哦,这不就是 yeoman 里 xxxx 嘛!我反问他:那你为何一开始看不懂呢?他说 yeoman 里不是这么写的……我又问:那你清楚 grunt 和 gulp 的差别吗?他说大致清楚,但是他从来没有这样写过 gulp,我说这有多大区别呢?gulp 本质上就是 node.js 程序,本质上还是 javascript 啊,他回答:我 node 很差……

是的,我想说的就是初学者因为 yeoman 比较容易上手,反而会忽略对于平台(node)和语言(javascript)本身的重视,一旦离开了 yeoman 这样的集成工具,就回到了什么都不会的年代。

#18 楼 @leopku yeoman 集成的东西太多了,感觉上来就玩这个就算跑起来了有很多东西也不明白所以~

#16 楼 @lips 发这个只是针对纯前端的项目,没有说要绑定到什么框架的意思。。

#11 楼 @lgn21st 试了试。。怎么总是有一种好像要慢一点的感觉。。

#2 楼 @hxtheone 就是配置起来方便些。。。但是如果都是参考别人的配置的话也没有什么太大的差别吧

#17 楼 @lgn21st rails 的江山,有 5% 被 node 阵营的 gulp 打下了,可以这么说么,哈哈

#24 楼 @lips 你想问题的方式很有意思,我不太关心这个阵营或者那个阵营,我只关心解决问题,以及解决问题的工具是否好用。

好复杂的配置文件...

#19 楼 @nightire 这个赶脚是个人问题吧,爱钻研的人快速上手后就会钻研细节。同样的道理,Rails 也是一个大成的集合,有些人在用有些人在钻研细节(这个没有对错之分),总不会因为这样就不推荐 Rails 了吧,哈哈,个人见解哈。

另外,也可以试试培养新人的钻研精神。😄

#27 楼 @leopku 呵呵,你在偷换概念哦~我说的是不向初学者推荐 yeoman,同样的道理放在 Rails 身上也是成立的。无论国内外都有这样的观点:Rails(或者 yeoman,或者其他类似的集大成者)方便、快捷、容易上手、适合快速搭建 xxx,但是不推荐初学者直接学习

有的人连最基本的 HTTP 都没过一遍就冲着 Rails“快”一头扎进去了,结果呢?排除个别人确实能力强三观正还能逐渐走得出来,又有多少人死在一堆坑里?

初学者就是初学者,有些事情必须得一步一步来。你说培养钻研精神那是绝对正确的,不过培养的时候不能忘了给他们恰当的环境和土壤。以前我也不太在乎这些,自从开始带实习生之后感触颇深:揠苗助长、欲速则不达。的确有很多工具不是为初学者设计的,遗憾的是绝大部分初学者没有这种分辨力和自律力,很容易就走上弯路尚不自知。

#26 楼 @huacnlee 是啊,一层套一层的。。。

#12 楼 @lgn21st 我觉得把简单灵活的命令行参数做成一个复杂难用的插件太蛋疼...

concat 不就是 cat 么... global command 的问题 thor 解决 streaming 就是重新发明了 shell 的 pipe orchestra 什么的依赖管理 make/rake 本来就带 watch 我们的编译工具里已经有 sass --watch, browserify --watch , 还有 listen 和 guard 等 gem 可以用

#32 楼 @luikore 我好像真的在什么地方见过有人写文章介绍用 make 替代 assets pipeline 编译的,不过没有流行起来,也许是有 Shell 编程背景和 C 背景的人比例少吧?

You need to Sign in before reply, if you don't have an account, please Sign up first.