grunt学习是我从网上看到的,感受不错,也试验过,便想整下来以便后期翻阅css
Grunt 依赖 Node.js 因此在安装以前确保你安装了 Node.js。而后开始安装 Grunt。html
实际上,安装的并非 Grunt,而是 Grunt-cli,也就是命令行的 Grunt,这样你就可使用 grunt 命令来执行某个项目中的 Gruntfile.js 中定义的 task 。可是要注意,Grunt-cli 只是一个命令行工具,用来执行,而不是 Grunt 这个工具自己。node
安装 Grunt-cli 须要使用 NPM,使用下面一行便可在全局范围安装 Grunt-cli ,换句话说,就是你能够在任何地方执行 grunt 命令:jquery
npm install -g grunt-cli
须要注意,由于使用 -g 命令会安装到全局,可能会涉及到系统敏感目录,若是用 Windows 的话,可能须要你用管理员权限,若是用 OS X / Linux 的话,你可能须要加上 sudo 命令。git
下面咱们新建一个项目目录,并新建一些文件,这里我准备了一份很简单的项目,放在了 Github 上,下面操做将以此来操做,你能够下载或者 clone 下来: https://github.com/yujiangshui/gruntxxgithub
这个 package.json 文件实际上是 Node.js 来描述一个项目的文件,JSON 格式。生成这个文件超级简单,推荐用命令行交互式的生成一下:npm
打开命令行,cd gruntxx
文件夹下面,输入指令 npm init
以后,就出来不少信息,而后开始填写项目名称,填写好了以后回车便可。其实这里你一路回车下去也无妨,可是建议你细细的填一下,不明白的跳过好了。json
填写好了以后,查看目录就会发现生成 package.json 文件了,这样就算生成好了。浏览器
其实就是一个文件而已,你以为这种方式麻烦,彻底能够新建一个文件,而后将相似下面的代码复制进去,改一下对应选项,保存成 package.json 文件就能够:sass
{ "name": "my-project-name", "version": "0.1.0", "devDependencies": { } }
最后我生成的代码以下:
{ "name": "gruntxx", "version": "0.0.1", "description": "学习 grunt", "repository": { "type": "git", "url": "https://github.com/yujiangshui/gruntxx.git" }, "author": "Jiangshui", "license": "MIT", "bugs": { "url": "https://github.com/yujiangshui/gruntxx/issues" }, "homepage": "https://github.com/yujiangshui/gruntxx" }
但这时咱们尚未在项目文件中安装 Grunt 以及相关任务插件。
就如今的这个示例项目而言,我打算让 Grunt 帮忙实现下面几个功能:检查每一个 JS 文件语法、合并两个 JS 文件、将合并后的 JS 文件压缩、将 SCSS 文件编译、新建一个本地服务器监听文件变更自动刷新 HTML 文件。
差很少就是这些,根据这些任务需求,须要用到:
它们的命名和文档都很规范,由于这些是官方提供的比较经常使用的插件。这些插件同时都是 NPM 管理的包,好比 grunt-contrib-concat - npm 你也能够在这上面看到用法等。
下面咱们就要在这个项目中安装这些插件,执行命令:
npm install grunt --save-dev
表示经过 npm 安装了 grunt 到当前项目,同时加上了 –save-dev 参数,表示会把刚安装的东西添加到 package.json 文件中。不信你打开 package.json 文件看下,是否是多了
"devDependencies": { "grunt": "^0.4.5" }
没错,这个的意思就是当前项目依赖 grunt,后面是它的版本,我们不用管。若是安装的时候没有添加 –save-dev 参数,这里就不会出现了,你须要自行添加上去。
下面咱们来安装 Grunt 的插件,固然,不须要一个个的安装,太麻烦了,咱们能够:
npm install --save-dev grunt-contrib-concat grunt-contrib-jshint grunt-contrib-sass grunt-contrib-uglify grunt-contrib-watch grunt-contrib-connect
等待一大串乱七八糟的下载状态,咱们把 grunt 和相关插件都安装好了,不信看下是否是多了一个 node_modules 文件夹?打开看下,里面就是我们刚安装的插件。
插件也装好了,开始写任务吧!既然是要程序来读取执行,必要要有必定的语法规范,下面来简单的说一下:
首先要明白,这是一个 JS 文件,你能够写任意的 JS 代码,好比声明一个 对象 来存储一会要写任务的参数,或者是一个变量看成开关等等。
而后,全部的代码要包裹在
module.exports = function(grunt) { ... };
里面。没有为何。
在这里面的代码,除去你本身写的乱七八糟的 JS,与 Grunt 有关的主要有三块代码:任务配置代码、插件加载代码、任务注册代码。
顾名思义,这三块代码,任务配置代码就是调用插件配置一下要执行的任务和实现的功能,插件加载代码就是把须要用到的插件加载进来,任务注册代码就是注册一个 task,里面包含刚在前面编写的任务配置代码。
这样,就能够用 grunt 来执行注册的一个 task 从而根据任务配置代码调用须要的插件来执行相应的操做。
下面来分别看一下这三块代码的写法。
例以下面代码:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } });
能够看出,具体的任务配置代码以对象格式放在 grunt.initConfig
函数里面,其中先写了一句 pkg: grunt.file.readJSON('package.json')
功能是读取 package.json 文件,并把里面的信息获取出来,方便在后面任务中应用(例以下面就用了 <%= pkg.name %>
来输出项目名称),这样能够提升灵活性。以后就是 uglify 对象,这个名字是固定的,表示下面任务是调用 uglify 插件的,首先先配置了一些全局的 options 而后新建了一个 build 任务。
也就是说,在 Uglify 插件下面,有一个 build 任务,内容是把 XX.js 压缩输出到 xx.min.js 里面。若是你须要更多压缩任务,也能够参照 build 多写几个任务。
至于怎么写出来 options 里面的参数和 build 里面的参数内容,这才是 grunt 学习的难点,你须要查看每一个插件的用法,根据用法来编写任务,能够看下 grunt-contrib-uglify 的官方文档,往下面拉你就能够看到参数和使用方法了。
这样,咱们就新建了一个基于 uglify 的任务 build,功能是把 src/<%= pkg.name %>.js
压缩输出 build/<%= pkg.name %>.min.js
。
这个就超级简单了,因为上面任务须要用到 grunt-contrib-uglify,当 grunt-contrib-uglify 安装到咱们的项目以后,写下下面代码便可加载:
grunt.loadNpmTasks('grunt-contrib-uglify');
插件也加载了,任务也布置了,下面咱们得注册一下任务,使用
grunt.registerTask('default', ['uglify']);
来注册一个任务。上面代码意思是,你在 default 上面注册了一个 Uglify 任务,default 就是别名,它是默认的 task,当你在项目目录执行 grunt 的时候,它会执行注册到 default 上面的任务。
也就是说,当咱们执行 grunt 命令的时候,uglify 的全部代码将会执行。咱们也能够注册别的 task,例如:
grunt.registerTask('compress', ['uglify:build']);
若是想要执行这个 task,咱们就不能只输入 grunt 命令了,咱们须要输入 grunt compress
命令来执行这条 task,而这条 task 的任务是 uglify 下面的 build 任务,也就是说,咱们只会执行 uglify 里面 build 定义的任务,而不会执行 uglify 里面定义的其余任务。
这里须要注意的是,task 的命名不能与后面的任务配置同名,也就是说这里的 compress 不能命名成 uglify,这样会报错或者产生意外状况
OK,加上这三块代码,咱们的示例 Gruntfile.js 就是这样子的:
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']); };
这就是官方那个坑爹示例,貌似 uglify 的参数好像不对,反正我以前学习的时候,无法运行这个配置,下面咱们来根据示例项目和咱们的需求配置一下。
先从简单的入手,咱们先来配置一下编译 Scss 文件的 task。先新建一个 Gruntfile.js 文件,把大致的配置结构复制进去:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('default'); };
应该能够看懂把?这里再也不赘述了,咱们来根据 Sass 文档,编写一个 Sass 任务 output :
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('default'); };
意思就是将 ./scss/style.scss
这个文件以 sassStyle 变量存储的方式编译成 根目录下面的 style.css 文件。
下面拿起命令行,cd 到当前文档目录,执行一下 grunt 命令,结果报错 undefined,没错,由于咱们的 default task 里面没有定义任何任务,而后执行 grunt outputcss
命令,提示编译 Scss 文件成功,固然前提是你的 Scss 语法正确,若是有问题就不会成功。
下面咱们打算先把 src 目录下面的两个 JS 文件合并起来,而后再用 jshint 检测一下是否有语法问题,若是正确,再用 uglify 对合并起来的文件进行压缩。
参照 grunt-contrib-concat 的官方文档,咱们能够写出下面的任务:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { options: { separator: ';', }, dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('default'); };
执行 grunt concatjs
以后,就会发现根目录多了一个 global.js 文件,里面是两个文件合并起来的。而后相似的继续看 uglify 和 jshint 的文档,咱们就能够根据需求写出下面任务:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { options: { separator: ';', }, dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, }, uglify: { compressjs: { files: { './global.min.js': ['./global.js'] } } }, jshint: { all: ['./global.js'] } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('compressjs',['concat','jshint','uglify']); grunt.registerTask('default'); };
其中注册了一个 compressjs 任务 grunt.registerTask('compressjs',['concat','jshint','uglify']);
意思就是依次执行 合并、检查、压缩 任务。咱们把刚生成的 global.js 文件删掉,在命令行执行 grunt compressjs
任务,结果 jshint 报错了:
grunt 遇到错误就退出了,就无法继续执行下面的任务。经过错误提示能够发现,是由于 concat 里面设置的参数——在两个文件合并间插入一个“;”——这原本是为了防止两个文件之间相互干扰设置的,结果没法被 jshint 验证经过,咱们能够删掉这个参数,或者设置 jshint 验证这两个文件,而后再进行合并。
为了方便,我删掉了这个参数。再执行一下,成功了,项目目录里面多了 global.js 和 global.min.js 文件。
小明看到这里,痛哭流泪,本身每次打开好几个网站,辛苦挨个粘贴复制新建,没想到输入一条命令就能够了。不过让他更伤心的还在后面,连这些命令都不用重复输入。
咱们能够经过 watch 来监听文件变更,当文件变化了(咱们编写保存了),自动执行某些任务。此处为了节约版面,我连自动刷新的任务一块写上去。根据 grunt-contrib-watch 和 grunt-contrib-connect 这俩文档,咱们能够写出下面的任务:
module.exports = function(grunt) { var sassStyle = 'expanded'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { output : { options: { style: sassStyle }, files: { './style.css': './scss/style.scss' } } }, concat: { dist: { src: ['./src/plugin.js', './src/plugin2.js'], dest: './global.js', }, }, uglify: { compressjs: { files: { './global.min.js': ['./global.js'] } } }, jshint: { all: ['./global.js'] }, watch: { scripts: { files: ['./src/plugin.js','./src/plugin2.js'], tasks: ['concat','jshint','uglify'] }, sass: { files: ['./scss/style.scss'], tasks: ['sass'] }, livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ 'index.html', 'style.css', 'js/global.min.js' ] } }, connect: { options: { port: 9000, open: true, livereload: 35729, // Change this to '0.0.0.0' to access the server from outside hostname: 'localhost' }, server: { options: { port: 9001, base: './' } } } }); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.registerTask('outputcss',['sass']); grunt.registerTask('concatjs',['concat']); grunt.registerTask('compressjs',['concat','jshint','uglify']); grunt.registerTask('watchit',['sass','concat','jshint','uglify','connect','watch']); grunt.registerTask('default'); };
添加了 connect 任务,用来新建一个本地服务器,以当前目录做为服务器根目录,而后添加 watch 任务,监听 Scss 文件变更,若是变了,执行一下 sass 任务,监听那俩 JS,若是变了,执行 合并、检查、压缩 任务,监听 html、css、js 文件,若是变更,livereload 自动刷新打开的页面。
而注册的 watchit task 就是咱们的终极 task,第一次执行,先编译 sass、再合并、检查、压缩、开启服务器、监听文件变更。咱们执行一下 grunt watchit
就能够看到效果了,你能够修改一下 scss 文件,把字体设置大一点,切换到浏览器的时候,就看到了实时刷新效果。也能够修改一下 JS,故意改错一下,会发现 jshint 会提示你出错了。
看到这里,小明和小红相拥而泣。。。。
项目开发完成以后,每每须要 push 到 Github 或者上传 FTP 等。或许其余人会接手你的项目继续开发,或者你会换台电脑进行开发。
当小明用 git 上传 Github 的时候,傻了眼,项目里 node_modules 文件夹下面的东西要十几M呢,这比我项目自己还大,上传下载都不方便。
其实这些插件和 grunt 不须要上传,由于有 package.json 这个文件记录了你这个项目中依赖的 grunt 插件,你只须要上传这个文件便可。下载下来以后,只须要在这个项目文件夹下面,输入命令 npm install
,NPM 会自动读取 package.json 文件,将 grunt 和有关插件给你下载下来,很方便的。
也不须要在本地上传的时候删除,用 git 的话,可使用 .gitignore 文件来过滤掉这个文件夹,禁止 git 追踪。
Grunt 就是这样一种任务自动运行器,应用好它能够减轻不少没必要要的人工操做,只须要专一 coding 就能够。甚至还有Grunt 插件帮你自动完成 CSS Sprite,更多功能还须要你本身去摸索。
新手看完本文,再看一下 Grunt 官方文档 应该没有太多疑问了,那就再看一遍把。固然也有中文版。
除了 Grunt 以外,同类型比较火的还有 Gulp 这个工具。其实两个东西的功能是同样的,只不过是任务配置 JS 的语法不一样,Gulp 的 Gulpfile.js 的写法更加通俗易懂,上手更快。可是 Gulp 的插件等感受不如 Grunt,Grunt 官方提供了一些常见的插件,知足大部分平常工做,并且可靠值得信赖,而 Gulp 好像没有太多官方出品,各类插件不太规范。简单的说,Grunt 和 Gulp 就像 iPhone 与 Android 同样,一个质量高学习难一点,一个学起来简单可是有点那个,你懂得。
此外,能够看一些高手的项目,你会发现更好的 Grunt 用法,好比 Yeoman 生成的项目,就有很完善的 Grunt 任务和插件,此外,jQuery 等也用 Grunt 进行打包,这些 Grunt 文件你均可以查看研究一下他们的写法和用法,受益不浅。
最后,若是你懒得跟着文章一点点的配置示例项目,你也能够跳转到示例项目的 grunt 分支,这里面是我配置好的,你须要先 npm install
而后就能够直接执行那些命令。