收录待用,修改转载已取得腾讯云授权


前言

在 web 前端开发中,我们会借助 Grunt、Gulp 和 Webpack 等工具的 Watch 模块去监听文件变化,那服务端应该怎么做?其实文件变化的监听依然可以借助构建工具,但我们还需要自动重启服务或者热重载。本文将介绍三种常见的方法。

方案一:fs.watch

使用 node 原生的 fs.watch 方法监听文件改动,所谓的“热重载”也不过是及时清除内存中的文件缓存。示例如下:

const fs = require('fs'),
path = require('path'),
projectRootPath = path.resolve(__dirname, './src'); const watch = project => {
require('./src'); // 启动 APP,自动检索到 src/index.js
try { // 监听文件夹
fs.watch(project, { recursive: true }, cacheClean)
} catch(e) {
console.error('watch file error');
}
}
// 清除缓存
const cacheClean = () => {
Object.keys(require.cache).forEach(function (id) {
if (/[\/\\](src)[\/\\]/.test(id)) {
delete require.cache[id]
}
})
}
// 启动开发模式
watch(projectRootPath);

注意:在服务器入口文件 src/index.js 中引用中间件时需要套一层函数,并使用 require 的方式引入模块才能清除缓存。比如:

// 引入中间件或控制器
app.use(async (ctx, next) => {
await require('./controllers/main.js')(ctx);
});
// 或引入路由
app.use(async (ctx, next) => {
await require('./router').routes()(ctx, next)
})

方案二:应用进程管理器

此处以 PM2 为例,supervisorforever 等类似的进程管理工具异曲同工,这里不再赘述。

PM2 是一款带有负载均衡功能的 Node 应用进程管理器,具有 —watch 配置项,用来监听应用目录的变化,一旦发生变化,立即重启。详见:Auto restart apps on file change。他是真正意义上的重启,不是热替换。

缺点:PM2 并不提供优雅的方式告知用户何时重启或者杀掉进程。

以下是一个简单的 PM2 配置 (开发环境) start.js,启动进程 node start.js

const pm2 = require('pm2');

pm2.connect(function(err) {
if (err) {
console.error(err);
process.exit(2);
} pm2.start({
"watch": ["./app"], // 开启 watch 模式,并监听 app 文件夹下的改动
"ignore_watch": ["node_modules", "assets"], // 忽略监听的文件
"watch_options": {
"followSymlinks": false // 不允许符号链接
},
name: 'httpServer',
script: './server/index.js', // APP 入口
exec_mode: 'fockMode', // 开发模式下建议使用 fockModel
instances: 1, // 仅启用 1 个 CPU
max_memory_restart: '100M' // 当占用 100M 内存时重启 APP
}, function(err, apps) {
pm2.disconnect(); // Disconnects from PM2
if (err) throw err
});
});

每次修改文件之后保存(Ctrl+S),会有个黑框闪一下,说明应用已经成功重启了。

方案三:chokidar + babel

chokidar 是对 fs.watch / fs.watchFile / fsevents 的一层封装。它的优势包括解决(出自 chokidar 文档):

1、在 OS X 下不能获取文件名;

2、在 OS X 下 Sublime 修改文件后不能获取到修改事件;

3、修改文件会触发两次事件;

4、不提供文件递归监听;

5、高 CPU 使用率;

6、…

这里使用 babel 的原因是想要支持最新的 js 语法,包括 ES2017、Stage-x,以及 import / export default 等模块语法。

下面提供一个完整的监听重载配置文件,并通过注释说明功能和意义。

const projectRootPath = path.resolve(__dirname, '..'),
srcPath = path.join(projectRootPath, 'src'), // 源文件
appPath = path.join(projectRootPath, 'app'), // 编译后输出文件夹
devDebug = debug('dev'),
watcher = chokidar.watch(path.join(__dirname, '../src')) // 启动 chokidar 监听文件改动
watcher.on('ready', function () {
// babel 编译文件夹目录
babelCliDir({
outDir: 'app/',
retainLines: true,
sourceMaps: true
}, [ 'src/' ]) // compile all when start require('../app') // 启动 APP(编译后的文件) // 添加监听方法
watcher
// 文件新增
.on('add', function (absPath) {
compileFile('src/', 'app/', path.relative(srcPath, absPath), cacheClean)
})
// 文件修改
.on('change', function (absPath) {
compileFile('src/', 'app/', path.relative(srcPath, absPath), cacheClean)
})
// 文件删除
.on('unlink', function (absPath) {
var rmfileRelative = path.relative(srcPath, absPath)
var rmfile = path.join(appPath, rmfileRelative)
try {
fs.unlinkSync(rmfile)
fs.unlinkSync(rmfile + '.map')
} catch (e) {
devDebug('fail to unlink', rmfile)
return
}
console.log('Deleted', rmfileRelative)
cacheClean(); //清除缓存
})
}) // 动态编译文件
function compileFile (srcDir, outDir, filename, cb) {
const outFile = path.join(outDir, filename),
srcFile = path.join(srcDir, filename); try {
babelCliFile({
outFile: outFile,
retainLines: true,
highlightCode: true,
comments: true,
babelrc: true,
sourceMaps: true
}, [ srcFile ], {
highlightCode: true,
comments: true,
babelrc: true,
ignore: [],
sourceMaps: true
})
} catch (e) {
console.error('Error while compiling file %s', filename, e)
return
}
console.log(srcFile + ' -> ' + outFile)
cb && cb() // 通常为清除缓存
}
// 清除缓存
function cacheClean () {
Object.keys(require.cache).forEach(function (id) {
if (/[\/\\](app)[\/\\]/.test(id)) {
delete require.cache[id]
}
})
console.log('App Cache Cleaned...')
}
// 监听程序退出
process.on('exit', function (e) {
console.log('App Quit')
})

注意:为了能让缓存失效,我们同样需要在 use 里包裹一层函数,并以 require 的方式引入路由

app.use(async (ctx, next) => {
await require('./router').routes()(ctx, next)
})

方案四:开发插件

nodemon 和 node-dev 都是可用于 node.js 开发版插件,提供简单易用的开发环境。以 nodemon 为例,全局安装或本地安装都可

npm install nodemon -g

然后通过 nodemon ./server.js localhost 8080 启动开发进程。独立、简单,好用!

详见:remy/nodemon

综上

每个方法都有不同的适用场景。如果想要尝试最新语法,推荐试用方案三;如果追求简单快捷,方案二是不错的选择。

这就结束了吗?

如果我既想用最新的语法特性,又需要像 PM2 那样简单,怎么办?babel 构建工具(如 webpack)对于每个前端开发并不陌生,再加一款 PM2 足以解决所有问题。


原文链接:https://www.qcloud.com/community/article/476280

Node Server零基础——开发环境文件自动重载的更多相关文章

  1. 精品教程--IOS零基础开发环境搭建

    下载源码 技术要点: 1. 启动XCODE开始开发 2. IOS项目文件结构分析 3. 添加视图label组件 4. 程序的入口以及启动流程 5. 源码详细的中文注释 ...... 详细介绍: 1. ...

  2. JAVA 基础开发环境 vscode 搭建 Windows下VSCode编译运行简单java

    JAVA 基础开发环境 vscode 搭建 来源 https://www.cnblogs.com/freewsf/p/7744728.html 对于使用 Visual Studio Code 的 Ja ...

  3. html5游戏开发-零基础开发《圣诞老人送礼物》小游戏

    开言: 以前lufy前辈写过叫“ HTML5游戏开发-零基础开发RPG游戏”的系列文章,在那里面我学习了他的引擎以及了解了游戏脚本.自从看了那几篇文章,我便对游戏开发有了基本的认识.今天我也以零基础为 ...

  4. 【转载】salesforce 零基础开发入门学习(一)Salesforce功能介绍,IDE配置以及资源下载

    salesforce 零基础开发入门学习(一)Salesforce功能介绍,IDE配置以及资源下载   目前国内已经有很多公司做salesforce,但是国内相关的资料确是少之又少.上个月末跳槽去了新 ...

  5. Centos 基础开发环境搭建之Maven私服nexus

    hmaster 安装nexus及启动方式 /usr/local/nexus-2.6.3-01/bin ./nexus status Centos 基础开发环境搭建之Maven私服nexus . 软件  ...

  6. [Android] 环境配置之基础开发环境(SDK/Android Studio)(转)

    [Android] 环境配置之基础开发环境(SDK/Android Studio)   博客: blog.csdn.net/qiujuer 网站: www.qiujuer.net 开源库: Geniu ...

  7. 零基础开发一款微信小程序商城

    零基础开发一款微信小程序商城 一个朋友问我能不能帮忙做个商城?我一个完整网页都写不出的 菜鸟程序员,我该怎么拒绝呢?好吧,看在小程序这么火的形势下,我还是答应了!找了个开源项目,差不多花了三天时间搞定 ...

  8. 【转载】salesforce 零基础开发入门学习(四)多表关联下的SOQL以及表字段Data type详解

    salesforce 零基础开发入门学习(四)多表关联下的SOQL以及表字段Data type详解   建立好的数据表在数据库中查看有很多方式,本人目前采用以下两种方式查看数据表. 1.采用schem ...

  9. 【转载】salesforce 零基础开发入门学习(三)sObject简单介绍以及简单DML操作(SOQL)

    salesforce 零基础开发入门学习(三)sObject简单介绍以及简单DML操作(SOQL)   salesforce中对于数据库操作和JAVA等语言对于数据库操作是有一定区别的.salesfo ...

随机推荐

  1. Xamarin Android权限请求

    Xamarin Android权限请求   Android权限规定了App是否可以访问特定的资源,如网络.电话和短信.在原有API 6.0之前,App在安全的时候,会请求一次权限.一旦安装后,App就 ...

  2. Mac os 下的文件权限管理

    Mac os 下的文件权限管理 命令 ls -l -A 结果 -rw-r--r-- 1 user admin 2326156 4 12 15:24 adb 横线代表空许可.r代表只读,w代表写,x代表 ...

  3. Sublime Text的使用技巧

    来到腾讯之后,基本上整个团队都在使用Sublime Text这款编辑神器.虽说自己以前在写python的时候略有接触过,但只是把它当做简单的文本编辑器.来到这边后,才逐渐的体会到这款神作的牛逼之处. ...

  4. Hibernate fetching strategies(抓取策略)

    抓取策略(fetching strategies)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的 ...

  5. QT学习笔记1:VS2015配置QT5.11

    由于工作关系,我又接触到了QT,在之前写过一篇博客是 在vs2017下配置openCV,当时用的是2015做的示范,现在就继续记录一下在VS2015下配置QT吧(VS2017配置是一样的) 第一部分: ...

  6. 【UOJ #108】【APIO 2013】TOLL

    http://uoj.ac/problem/108 好神的一道题啊. 原图边权互不相同是重点! 如果有一个点集,有两组边集,要求这两组边集的并集的最小生成树,可以对两组边集分别求一下最小生成树构成新的 ...

  7. [JSOI2007]文本生成器 --- AC自动机 + DP

    [JSOI2007]文本生成器 题目描述: JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版. 该软件可以随机 ...

  8. AOP 面向切面 记录请求接口的日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点, ...

  9. Markdown 简明语法手册 - 作业

    目录 Cmd Markdown 简明语法手册 1. 内容目录 2. 标签分类 3. 删除线 水平线--- 1. 斜体和粗体 2. 分级标题 标题1 标题2 标题3 3. 外链接 4. 无序列表 5. ...

  10. C#获取picturebox图片路径

    path = ofd.FileName; filename = ofd.SafeFileName; lu="E:\\鹿瑶\\Csharp\\DB学生报名系统\\baomingDemo\\bi ...