Nodemon自动重启后浏览器实时加载的两个问题

概述

nodemon管理的服务器一般会自动监听(可配置具体监听文件)服务器的文件的修改并重启子任务,一般用于监听一个http服务器子应用,为了方便开发通常会在restart事件中执行livereload(浏览器端自动刷新),但是监听文件的类型和restart事件中却有一些问题。

注意为了结合gulp使用,这里使用的是gulp-nodemon这个包。

livereload

为了便于开发,浏览器端使用livereload插件自动刷新以获得服务器端的代码变化(笔者使用chrome,其他部分浏览器也有插件)。

nodemon的restart事件

nodemon监听指定的文件后,也可以配置指定的响应事件,在前端开发环境中,一般的需求是结合livereload做到监听源代码,保存以后响应一系列的后续任务,并最终通知浏览器重加载。

查阅nodemon官方文档中的事件可知,restart应该就是我们要监听响应的事件。因此直观地解决方案就是在restart事件中响应livereload的逻辑。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
gulp.task('watch', ()=> {
livereload.listen()
nodemon({
script: 'index.js',
tasks: [
'compile',
],
})
on('restart', ()=> {
gulp.src('index.js')
.pipe(livereload());
});
});

上述配置理论上应该没有问题,实际上浏览器也会自动刷新,但刷新页面的结果却是浏览器无法打开页面的状态。

因为nodemon还在没有完成启动,但livereload已经发出了刷新页面的通知。可以得出结论,restart事件并不是在nodemon重启服务器后的响应事件,而是重启开始时的响应事件,服务器还没有来得及完成重启。这显然不是我们想要的结果。

一种最暴力直接的解决方案就是使用setTimeout延迟一定时间再发出命令。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gulp.task('watch', ()=> {
livereload.listen()
nodemon({
script: 'index.js',
tasks: [
'compile',
],
})
on('restart', ()=> {
setTimeout(()=> {
gulp.src('index.js')
.pipe(livereload());
}, 8e2);
});
});

这种方案明显不够完美,但实际上,这是笔者目前在StackOverflow找到的绝大多数解决方案。

GitHub上有人给出了另一种解决方案。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gulp.task('watch', function() {
livereload.listen()
nodemon({
script: 'index.js',
stdout: false
}).on('readable', function() {
this.stdout.on('data', function(chunk) {
if (/^listening/.test(chunk)) {
livereload.reload()
}
process.stdout.write(chunk)
})
})
})

这里监听的是readable事件,监听readable事件有一个需要注意的地方是需要配置stdout配置为false。

但遗憾的是,笔者尝试这种方式后并没有成功。

虽然目前没有找到完美的解决方案,但是由于这个是开发环境,能实现自动自动加载就能方便开发人员的工作,而且经过笔者尝试,800毫秒是一个合适的值,太快的话服务器依然未能启动,太慢的话就更影响开发体验。在没有找到更完美的解决方案之前,先只能用这种方案了。

jsx文件

笔者现在使用的前端框架主要是React,不可避免开发中会有大量jsx文件。但配置nodemon监听jsx文件却并没有作用。一般来讲nodemon就是监听指定的文件,发现变化以后根据配置,重新启动服务器,但这里却监听不到jsx文件的变动。

经过一番尝试并依然失败之后,笔者不得不放弃使用nodemon监听源文件,转而让监听源文件工作交给gulp,nodemon改为监听打包编译后的文件。

比如在上面的配置中,笔者还配置了在每次在启动服务器之前,都先将执行提前写好的compile的gulp任务,因为工作中使用的是es6,需要经过babel的一层解析(这里只是举个例子,实际上使用的是webpack),因此使用nodemon监听源文件是可以的,但是它却不响应jsx的修改。

修改后的配置,将新的监听任务独立出来交给gulp,让gulp来做编译打包的工作。打包完成之后必然生成新的文件,nodemon就监听这个新文件生成的目录。

1
2
3
4
5
6
7
gulp.task('watch:src', ()=> {
gulp.watch([
'index.js',
], [
'compile',
]);
});
1
2
3
4
5
6
7
8
9
10
11
12
gulp.task('watch', ()=> {
livereload.listen()
nodemon({
script: 'bundle.js',
})
on('restart', ()=> {
setTimeout(()=> {
gulp.src('index.js')
.pipe(livereload());
}, 8e2);
});
});

这样也很好地解决了监听jsx的问题。

参考

  1. nodemon
  2. gulpjs
  3. gulp-nodemon
  4. livereload