Node 的调试方法有不少,主要分为安装 node-inspect 包调试、用 Chrome DevTools 调试和 IDE 调试,能够在官网的 Docs Debugging Guide 查看安装方法。html
下面介绍使用 Chrome DevTools 调试的方法,首先安装 Chrome Extension NIM,打开 Inspect 入口页面 chrome://inspectnode
写一个简单 debug.js 测试文件:webpack
// apiTest/debug.js
console.log("this is debug test")
function test () {
console.log("hello world")
}
test()
复制代码
使用node --inspect-brk
来启动脚本,-brk
至关于在程序入口前加一个断点,使得程序会在执行前停下来web
$ node --inspect-brk apiTest/debug.js
Debugger listening on ws://127.0.0.1:9229/44b5d11e-3261-4090-a18c-2d811486fd0a
For help, see: https://nodejs.org/en/docs/inspector
复制代码
在 chrome://inspect 中设置监听端口 9229(默认),就能够看到能够 debug 的页面:chrome
(function (exports, require, module, __filename, __dirname) {
console.log("this is debug test")
function test () {
console.log("hello world")
}
test()
});
复制代码
若是咱们使用node --inspect
来启动脚本,那整个代码直接运行到代码结尾,没法进行调试,但此时 Node 还进程没有结束,因此能够在 http://127.0.0.1:9229/json/list 查询 devtoolsFrontendUrl ,复制此 Url 到 Chrome 上进行调试。json
看到使用 Chrome DevTools 的调试方法仍是比较复杂的,一些 IDE 都支持直接断点调试,推荐WebStorm、VScode。segmentfault
在 Node 中经常使用的全局方法有 CommonJS、Buffer、process、console、timer 等,这些方法不须要 require
引入 API 就能够直接使用。后端
若是但愿有属性或方法能够*“全局使用”*,那就将它挂载在 Node 的global
对象上:api
global.gNum = 300
console.log(gNum); // 300
复制代码
在 Node 中全部模块均可以使用这些全局变量,如下就介绍 Node 中的全局变量数组
Node CommonJS 模块规范根据实现了module
、exports
和require
模块机制。Node 对每一个文件都被进行了模块封装,每一个模块有本身的做用域,如在 debug 时看到的:
(function (exports, require, module, __filename, __dirname) {
// some code
});
复制代码
模块机制中的 __dirname
、__filename
、exports
、module
、require()
这些变量虽然看起来是全局的,但其实它们仅存在于模块范围。须要注意的几点是:
module
变量表明模块自己require()
方法引入外部模块到当前的上下文中module.exports
属性表明模块对外接口,默认的快捷方式exports
简单的使用方式以下:
/* common_exports.js */
exports.num = 100
exports.obj = {
a : 200
}
exports = {
count : 300
}
/* common_require.js */
const mod = require('./common_exports')
console.log(mod) // { num: 100, obj: { a: 200 } }
console.log(mod.count) // undefined
复制代码
注意到上例中的mod.count
为undefined
,这是由于exports
只是module.exports
的引用,能够给exports
添加属性,但不能修改exports
的指向。
更深刻的了解模块机制能够看 【Node】先后端模块规范与模块加载原理
process 包含了进程相关的属性和方法,Node 的 process 文档 中的内容特别多,列举几个经常使用方法。
Node 进程启动时传递的参数都在 process.arg
数组中:
// process.js
const {argv , execPath} = process
argv.forEach((val, index) => {
console.log(`${index}: ${val}`)
})
console.log(execPath)
复制代码
能够在执行 process.js 时传递其余参数,这些参数都会保存在argv
中:
$ node apiTest/process.js one=1 --inspect --version
0: /usr/local/bin/node
1: /Users/mobike/Documents/webProjects/testNode/apiTest/process.js
2: one=1
3: --inspect
4: --version
/usr/local/bin/node
复制代码
process.argv
第一个参数就是 process.execPath
,即调用执行程序 Node 的路径,第二个参数时被执行的 JS 文件路径,剩下的就是自定义参数。
process.env
是包含运行环境各类参数的对象,能够直接输出env
查看全部参数信息,也能够输出某个属性:
const env = process.env
console.log(env.PATH) // /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/Documents/webProjects/testNode/node_modules/.bin
console.log(env.SHELL) // /bin/zsh
复制代码
在 webpack 打包过程当中经常使用process.env.NODE_ENV
判断生产环境或开发环境,process.env
是没有NODE_ENV
这个属性的,你能够在系统环境变量中配置,也能够在项目程序直接设置process.env.NODE_ENV=‘dev’
。
process.cwd()
方法返回 Node.js 进程的当前工做目录,和 Linus 命令$ pwd
功能同样:
// process.js
console.log(process.cwd()) // /Users/Documents/webProjects/testNode
复制代码
$ node process.js
/Users/WebstormProjects/testNode
$ pwd
/Users/WebstormProjects/testNode
复制代码
Node 中的计时器方法与 Web 浏览器中的JS 计时器相似,但内部实现是基于 Node 的 Event Loop。Node 中的计时器有setImmediate()
、setTimeout()
、setInterval()
。
在 Node 中有一个轻量级的process.nextTick()
异步方法,它是在当前事件队列结束时调用, setImmediate()
是当前 Node Event Loop 结束时当即执行,那执行顺序有什么区别呢?
下面举例说明process.nextTick(fn)
与setImmediate(fn)
与setTimeout(fn,0)
之间的区别:
// timer.js
setImmediate(()=>{
console.log("setImmediate")
});
setTimeout(()=>{
console.log("setTimeout 0")
},0);
setTimeout(()=>{
console.log("setTimeout 100")
},100);
process.nextTick(()=>{
console.log("nextTick")
process.nextTick(()=>{
console.log("nextTick inner")
})
});
复制代码
看下执行结果:
$ node timer.js
nextTick
nextTick inner
setTimeout 0
setImmediate
setTimeout 100
复制代码
process.nextTick()
中的回调函数最快执行,由于它将异步事件插入到当前执行队列的末尾,但若是process.nextTick()
中的事件执行时间过长,后面的异步事件就被延迟。
setImmediate()
执行最慢,由于它将事件插入到下一个事件队列的队首,不会影响当前事件队列的执行。当setTimeout(fn, 0)
是在setImmediate()
以前执行。
Buffer 对象用于处理二进制数据流。JS 没有处理二进制的功能,而 Node 中的一部分代码是由 C++ 实现的,全部 Node 中的 Buffer 性能部分用 C++ 实现,非性能部分由 JS 封装。
Buffer 实例相似整数数组,元素为十六进制的两位数(0~255),而且挂载在 global 对象上不须要 require就能使用。
最新的 Buffer API 使用Buffer.alloc(length, value)
建立固定长度为 length 的 Buffer 实例,value 默认填充 0,使用Buffer.from()
将其它类型数据转为 Buffer:
console.log(Buffer.alloc(5)) // <Buffer 00 00 00 00 00>
console.log(Buffer.alloc(5, 44)) // <Buffer 2c 2c 2c 2c 2c>
console.log(Buffer.from([3, 4, 5])) // <Buffer 03 04 05>
console.log(Buffer.from('test')) // <Buffer 74 65 73 74>
console.log(Buffer.from('测试')) // <Buffer e6 b5 8b e8 af 95>
复制代码
注意到字符串转 Buffer 时英文占一位,中文占三位,而不是四位,当中文乱码的时能够考虑没有正确读取 Buffer 流。
Buffer 类提供几个静态方法,Buffer.byteLength()
计算长度,Buffer.isBuffer()
作验证,Buffer.concat()
拼接 Buffer 实例:
const buf1 = Buffer.from([3, 4, 5])
const buf2 = Buffer.from('test')
console.log(Buffer.byteLength('test')) // 4
console.log(Buffer.byteLength('测试')) // 6
console.log(Buffer.isBuffer('test')) // false
console.log(Buffer.isBuffer(buf1)) // true
console.log(Buffer.concat([buf1, buf2])) // <Buffer 03 04 05 74 65 73 74>
复制代码
除此以外,Buffer 实例也有经常使用的属性和方法,相似 JS 中的 String,有length
、toString('base64')
、equals()
、indexOf()
等。
以上就是 Node 全局变量的概述,其余的 API 或内置模块都须要·
require('xxx')
引入使用,咱们能够在 nodejs.cn 中查看关于 Global API 更详细的介绍。
path 是处理和路径相关问题的内置 API,能够直接require('path')
使用。如下示例经常使用的 path 方法。
对路径的处理经常使用path.normalize()
规范路径、path.join()
拼接路径,以及使用path.resolve()
将相对路径解析为绝对路径:
const path = require('path')
console.log(
path.normalize('//asd\/das'), // /asd/das
path.join('user', 'local'), // user/local
path.resolve('./')) // /Users/Documents/webProjects/testNode/apiTest
复制代码
解析某个路径,能够用path.basename()
获得文件名称,path.extname()
获得后缀扩展名,path.dirname()
获得目录名:
const path = require('path')
const filePath = 'webProjects/testNode/apiTest/path.js'
console.log(
path.basename(filePath), // path.js
path.extname(filePath) // .js
path.dirname(filePath), // webProjects/testNode/apiTest
)
复制代码
以上解析路径方法获得某个值,还可使用path.parse()
彻底解析路径为一个对象,path.format()
反向操做:
let sp = path.parse(filePath)
console.log(sp)
// { root: '',
// dir: 'webProjects/testNode/apiTest',
// base: 'path.js',
// ext: '.js',
// name: 'path' }
console.log(path.format(sp))
// webProjects/testNode/apiTest/path.js
复制代码
除此以外,还有对于系统路径的操做,使用path.sep
取得路径分隔符,路径片断分隔符,POSIX 上是/
, Windows 上是\
,path.delimiter
取得系统路径定界符,POSIX 上是:
,Windows 上是;
,示例以下:
console.log(filePath.split(path.sep))
// [ 'webProjects', 'testNode', 'apiTest', 'path.js' ]
console.log(process.env.PATH) // 系统路径配置
// /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
console.log(process.env.PATH.split(path.delimiter))
// [ '/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin' ]
复制代码
以上就是 Node 对路径的经常使用操做,须要注意的是,在获取路径时有几种方式,获得的路径是不一样的:
__dirname
、__filename
:老是返回文件绝对路径;process.cwd()
或 $ pwd
:返回执行 Node 命令的文件夹;path.resolve('./')
:是相对 Node 启动文件夹,在require()
中./
是相对于当前文件夹;大部分 Node API 都采用异步事件驱动,全部能触发事件对象都是 EventEmitter 类的实例,经过 EventEmitter.on()
绑定事件,而后经过 EventEmitter.emit()
触发事件。
// apiTest/events.js
const Events = require('events')
class MyEvents extends Events{
}
const event = new MyEvents()
event.on('test-event',()=>{
console.log('this is an event')
})
event.emit('test-event')
setInterval(()=>{
event.emit('test-event')
},500)
复制代码
执行以上代码会一直连续处罚 test-event 事件,固然还能够传递事件参数,而且能够传递多个参数。修改上诉代码以下:
event.on('test-event', (data, time) => {
console.log(data,time)
})
event.emit('test-event', [1, 2, 3], new Date())
复制代码
$ node apiTest/events.js
[ 1, 2, 3 ] 2019-04-23T07:28:00.420Z
复制代码
同一个事件监听器能够绑定多个事件,触发时按照绑定顺序加入执行队列,而且可使用EventEmitter.removeListener()
删除监听器的事件:
function fn1 () {
console.log('fn1')
}
function fn2 () {
console.log('fn2')
}
event.on('multi-event',fn1)
event.on('multi-event',fn2)
setInterval(()=>{
event.emit('multi-event')
},500)
setTimeout(()=>{
event.removeListener('multi-event', fn2)
}, 600)
复制代码
$ node apiTest/events.js
[ 1, 2, 3 ] 2019-04-23T07:39:11.624Z
fn1
fn2
fn1
fn1
...
复制代码
Node 文件模块经过require('fs)
使用,所用方法都有同步和异步方法。
文件系统中的异步方法,第一个参数保留给异常,操做成功时参数值为null
或undefined
,最后一个参数就是回调函数。例如读取文件的fs.readFile()
和写文件的fs.writeFile()
示例以下:
const fs = require('fs')
fs.readFile('./apiTest/fs.js', (err, data) => {
if (err) throw err
console.log('readFile done!!!')
})
fs.writeFile('./apiTest/fs.txt', 'this is test file', {
encoding: 'utf8'
}, (err) => {
if (err) throw err
console.log('writeFile done!!!')
})
复制代码
推荐 nodejs.cn 中的 Docs API 中文版查看更多 Node API 的使用。
加油呢少年~