在不久前我曾写了一篇 应用r.js来优化你的前端 的文章,为大家介绍了r.js这个实用工具,它可以很好地压缩、合并前端文件并打包整个项目。但是如果将r.js放到项目中,我们不得不顾及到一个问题——项目每维护一次,就需要维护的人员输入一次rjs运行口令重新打包项目,自然是非常繁琐的事情。另外如果我们的项目用sass来写样式,可能还得外开一个koala。如果我们能让项目自己处理自己的所有事宜,那一切就美好多了。
针对上述问题,今天我们就利用更为大众的工具——Grunt,来自动化地处理前端事务(其实rjs也是使用了Grunt的concat和uglify等插件)。
我们依旧使用一个非常简单的、名为before的项目(你可以在我的Github下载这个案例)来作为示例,它是一个很纯净的项目,还没有使用任何前端工具对其进行操作:
你可以试着运行根目录下的index.html文件,它会显示一张幻灯片。
接下来我们将在before项目上使用Grunt自动化前端事务。
操作需求
由于Grunt是运行与node上的,故要求你的电脑安装了nodeJS(点我下载);
由于我们这边使用了sass来写样式,故要求你的电脑安装了Ruby(注意Mac是默认安装有Ruby的,如果是Windows或其它系统请点此下载安装)。
安装时建议把三个可以勾选的选项都勾上(特别要勾选上第二个,这样才会添加环境变量),安装后点击 “所有程序” - “Ruby.13...” - “start command prompt with Ruby”,打开Ruby命令行界面并输入指令:
gem install sass
注意该指令必须要在FQ的情况下才执行,不然可能会导致无法安装sass的错误:
不过有时候系统可能会提示这个错误信息:
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://bb-m.rubygems.org/gems/multi_json-1.3.2.gem)
解决方法是在这里下载证书(http://curl.haxx.se/ca/cacert.pem),比如下载到 D:/ruby/cacert.pem, 然后再环境变量里新建一个SSL_CERT_FILE这个环境变量,并把变量值设为这个文件路径即可:
另外,我们后续编译sass的时候,若.scss文件中存在中文注释,Ruby可能会因为识别不了中文编码而导致出错。不过解决方法很简单,点击此处查看
安装GRUNT CLI
我们打开 Node.js command prompt 进入node命令行界面:
在命令行界面输入以下代码并回车运行,此举是安装Grunt全局命令行,从而我们可以在电脑任何位置调用grunt命令语句:
npm install -g grunt-cli
Grunt配置
Grunt的入门其实可以参考官方入门,虽然这篇官方指引写的很烂,却也告诉我们,Grunt配置离不开两个文件—— package.json 和 Gruntfile.js ,我们一般都把它们放置在项目根目录上 。
其中 package.json 作用是记录项目的名称(随便起个名字就行)、版本号(随便起个数字串就行)和插件依赖,创建package.json完全不用手动,我们只需要把node命令行定位到项目下(假设我们把上述的before项目放在E盘),然后输入以下指令并回车执行,便会在项目根目录自动创建一个初始化的 package.json 文件:
npm init
回车后node会问你若干个问题,包括项目名啊版本号啊什么有的没的,你有心情可以写一写,没心情就一路按回车下来即可:
这时你会发现根目录下就这样多了个叫package.json的文件:
里面有node帮我们预先定义好的json代码。不过我各人并不喜欢使用 npm init 这种婆妈的形式来自动生成package.json,还不如自己手动创建一个package.json,并输入简洁的代码:
{ "name": "before", "version": "1.0.0" }
是的,初始化咱只要就这么俩行即可,其实我甚至希望能简短到只有 { } 即可。可惜 name 和 version 对于 package.json 来说是非常重要的俩个属性,缺一不可(可以查看这里了解更多package.json属性的作用)
安装插件
Grunt只是一个载体,实际要用起来得使用各种五花八门的grunt插件(有点类似在Sublime上我们可以安装并使用各种sb插件)。
而我们的before项目需要运用到下面几个插件:
grunt —— 这个不用说了,就是Grunt它自己本身;
grunt-contrib-uglify —— 作用是压缩项目js文件;
grunt-contrib-sass —— 作用是编译项目中的sass为(压缩版的)css文件;
grunt-contrib-watch —— 作用是实现我们一开始需求的“自动化”!是最重要的一个插件之一!它会监听需要处理的文件的变动,一旦有变动就会自动执行相应处理。但是它有一个问题,就是每当监听到一处变动时,就会大费周章地把所有被监听的文件都处理一遍;
grunt-newer —— 作用是处理上方watch的毛病,让watch在监听到某个文件变动时,仅仅对变动的文件进行事务处理。
然而在node中,安装grunt插件的命令为:
npm install <插件名> --save-dev
其中后面的 “--save-dev” 表示保存为依赖,即在安装好插件后,会将此插件依赖写入package.json文件中(记得我们前面说到package.json的作用之一是记录项目的插件依赖么)。
于是我们在项目的根目录下分别运行下述命令安装插件:
npm install grunt --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-sass --save-dev
npm install grunt-contrib-watch --save-dev
运行时可能会提示如下的警告:
这是因为咱在package.json里没有写一些description等婆妈的东西,故这里的WARN不痛不痒直接无视它即可。
插件安装好后会发现项目根目录上多了个叫“node_modules”的文件夹,是用来存放我们安装好的插件的。另外我们打开package.json,会发现它已经自动加入了插件依赖信息:
配置Gruntfile.js文件
开始我们就提到grunt有2个不可缺少的文件 —— package.json 和 Gruntfile.js 文件,package.json咱已轻松搞定,下面新建一个Gruntfile.js 文件并输入以下代码:
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), //上面是固定好的,不用管它,照搬即可。 //下面是各插件事务的配置,顺序不分先后 sass: { // grunt-contrib-sass的事务定义 tocss: { options: { style: 'compressed', //以压缩模式编译css,这样咱们也没必要使用grunt-contrib-cssmin插件了 sourcemap:'none' //设置不要配套输出map文件 }, files: [{ expand:true, cwd:'css',//css目录下 src:'**/*.scss',//所有css文件 dest: 'css',//输出到此目录下 ext: '.css' }] } }, uglify: { // grunt-contrib-uglify的事务定义 compressjs: { files: [{ expand:true, cwd:'js',//js目录下 src:'**/*.js',//所有js文件 dest: 'output/js'//输出到此目录下 }] } }, watch: { // grunt-contrib-watch的事务定义 all: { files: ['css/**/*.scss','js/**/*.js'], tasks: ['newer:sass','newer:uglify'] } } }); //grunt.loadNpmTasks() 是告诉Grunt,咱们要使用哪些插件,顺序不分先后 grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-newer'); //grunt.registerTask('default', [])是告诉Grunt,我们在node命令行输入grunt指令后要执行哪些事务 grunt.registerTask('default', ['newer:sass','newer:uglify','watch']); //注意 "newer:XXX"是插件grunt-newer的事件定义,表示对冒号后面的事务生效 };
Gruntfile.js的配置或许算是配置Grunt的一个难点吧,它的作用是告诉Grunt要使用哪些插件(安装了不代表一定要用对吧,当然要用的话一定得安装)、每个插件又要进行怎样的事务操作。
其实Gruntfile.js最最简单的初始化框架是这样的:
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), 插件事务名称: { ........//事务定义 } }); //grunt.loadNpmTasks() 是告诉Grunt,咱们要使用哪些插件,顺序不分先后 grunt.loadNpmTasks('插件名'); //grunt.registerTask('default', [])是告诉Grunt,我们在node命令行输入grunt指令后要执行哪些事务 grunt.registerTask('default', ['插件事务名称']); };
稍微推敲下,很容易理解代码的含义。如若了解更灵活、复杂的Gruntfile.js配置,可以点此查看指引。
一切都捣鼓好后,我们可以直接在node命令行(依旧定位在根目录下哦,其实可以不用再唠嗑了)输入
grunt
回车执行后便进入自动化执行状态,只要有js或者scss文件变动,变动的文件就会重新被压缩或编译:
像本章的例子是使用了sass来写样式,如果你只写原生的css,又希望能压缩样式文件,那么可以使用 grunt-contrib-cssmin 插件,其具体配置可以点此查看,本文就不赘述了。
共勉~