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 的使用。
加油呢少年~