用Grunt与livereload构建实时预览的开发环境

自从用了yeoman来开发angular.js之后,就喜欢上这个工具了,顺带也了解了整个workflow所用的所有工具,yeoman给我最深的印象就是它可以在开发的时候启动一个localhost的server来预览你的前端项目,并且能够实时反应你对文件的修改。想想把,你不在需要依赖昂贵又不准确的IDE进行WYSIWYG开发,也不需要自己动手去按F5不停的刷新页面,还要担心缓存问题。现在只要在命令行运行 grunt serve 就可以做到了 这么好的功能当然不是yeoman特有的功能,只要你会用Grunt就可以做到。如果你还不熟悉Gruntjs那么,它的官方页面可以帮你快速熟悉常用配置(是的,你不需要知道怎么写grunt plugin)

我们需要哪些Grunt插件?

过去这个任务需要connect-livereload, grunt-contrib-connect, grunt-contrib-watch来配合完成. 感谢connect和grunt插件的开发者,我们现在只需要2个插件就可以做到这一切了:

  • grunt-contrib-connect, 用来充当一个静态文件服务器,本身集成了livereload功能,因此不再需要connect-livereload中间件
  • grunt-contrib-watch, 用来监视文件的改变,然后执行一些任务,同时保持 grunt-contrib-connect 的服务器一直开启

为了不用不厌其烦的写grunt.loadNpmTask(),我们使用 load-grunt-tasks 来帮助我们自动加载这些插件,为了能看到grunt任务执行时间,我们加入了 time-grunt 插件,这几个插件对于本文的目的来说都不是必须的

下面是配置好的package.json

1
2
3
4
5
6
7
8
9
10
11
{
"name": "grunt-livereload-demo",
"version": "0.0.1",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-connect": "~0.6.0",
"grunt-contrib-watch": "~0.5.3",
"load-grunt-tasks": "~0.3.0",
"time-grunt": "~0.2.9"
}

}

接下来运行 npm install 来安装就可以了。

配置Gruntfile

现在假设你已经知道Gruntfile的结构。那么让我们开始先搭建一个可以serve静态文件的服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module.exports = function(grunt) {

// Load all grunt tasks automatically
require('load-grunt-tasks')(grunt);

// Time how long grunt task take. Can help when optimizing build times
require('time-grunt')(grunt);

//Configure grunt
grunt.initConfig({

// The actual grunt server settings
connect: {
options: {
port: 9000,
hostname: 'localhost', // Change this to '0.0.0.0' to access the server from outside.
keepalive: true // keep the server alive. so the grunt task won't stop
},
all: {
options: {
open: true,
base: [
'examples' // This is the base file folder. we suppose our index.html is located in this folder
// replace with the directory you want the files served from
]
}
}
}
});

// Creates the 'serve' task
grunt.registerTask('serve', [
'connect:all'
]);
};

我们用grunt-contrib-connect创建了一个静态服务器,并且能自动打开浏览器。我们把配置好的任务加入到自己创建的serve任务里,别在意这里只有一个子任务,我们接下来会加入其他任务到这个serve任务里。现在你可以在命令行里运行 grunt serve (从Gruntfile所在的目录)。grunt会自动打开浏览器并访问 http://localhost:9000 并保持服务器一直运行下去(任务不会结束)。

Watch和livereload

虽然我们已经配置好了一个静态文件服务器。但是 grunt-contrib-connect 并不会帮助我们监视文件变化并自动加载,我们需要 grunt-contrib-watch 来完成这项工作,并触发 grunt-contrib-connect 里面的livereload功能。所以我们要修改一下Gruntfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
module.exports = function(grunt) {

// Load all grunt tasks automatically
require('load-grunt-tasks')(grunt);

// Time how long grunt task take. Can help when optimizing build times
require('time-grunt')(grunt);

//Configure grunt
grunt.initConfig({

// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729 // This does not perform live reloading. this port is used by watch task to trigger a live reloading action.
},
all: {
options: {
open: true,
base: [
'examples'
]
}
}
},

//Watch files for changes, and run tasks base on the changed files.
watch: {

livereload: {
options: {
livereload: '<%= connect.options.livereload %>' // this port must be same with the connect livereload port
},
// Watch whatever files you needed.
files: [
'examples/*.html',
'examples/styles/{,*/}*.css',
'examples/scripts/(,*/}*.js',
'examples/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
}
});

// Creates the 'serve' task
grunt.registerTask('serve', [
'connect:all',
'watch'
]);
};

我们新修改的配置把connect任务的keepalive选项去掉了,换成livereload。因为watch会帮我们让grunt task挂起而不停止,所以不再需要keepalive。这里我们将livereload的端口号设置为35729。你也可以设置成其他的端口,不过必须要在watch的livereload选项里设置成相同的端口。 新加入了一个watch任务,这里我们配置了livereload的端口。配置了需要监视的文件。当这些文件发生改变时,watch任务就会触发livereload。让浏览器刷新页面。最后,我们把这两个任务按照先后顺序组合成serve任务,注意watch必须在后面,因为watch之后的任务永远不会被执行,同时我们也需要watch帮我们保持服务器一直运行。

接下来运行 grunt serve 就可以看到一个跟刚才一样的页面在浏览器窗口中打开了。不同的是,这次你修改任何监视范围内的文件,都会实时的反映在浏览器上,可以说做到的了实时预览。当你需要结束服务器的时候,使用Ctrl+C。 watch并不止能做这些,它还可以让less自动编译,自动运行jshint检查js文件的语法。connect还可以和grunt-connect-proxy结合来制作本地代理访问其他域名的api而不用处理跨域问题。当你觉得某种开发方式不够酷的时候,想想grunt吧。如果你没有找到合适的插件,就自己编写一个。Have a good day!