学Node.js实际上就是另外一个环境的JS, 所以依然要看一看基础如何操作. 这里目的是要写一个小程序, 会用到最基本的内容, 比如命令行, JSON存储数据等.
- Node.js 命令行读取参数
- 用JSON存储数据
- 简单app - 添加note功能
- 简单app - 查看和删除note功能
- 用箭头函数重构
- 查找功能
- Node.js 的 debug功能
Node.js 命令行读取参数
动态语言经常就有直接就可以使用的读取输入的函数, 好比Python的内建函数.
Node.js有一个内建的全局对象process, 用其的.argv参数就可以获取命令行模式运行node时候传入的参数, 和静态语言的args一样, 这个process.argv也是一个数组, 放了字符串形式的所有内容.
其中第一个固定是node.exe, 第二个是js文件名称, 第三个(也就是索引2)就是实际的参数. app.js改成如下试验一下:
const args = process.argv;
for (let eachArg of args) {
console.log(eachArg);
}
可以任意执行这个文件加上各种参数:
node app.js -l x fdkj
在我的机器上输出如下:
D:\coding\notes-app>node app.js -l x fdkj
D:\software\node\node.exe
D:\coding\notes-app\app.js
-l
x
fdkj
如果靠process.argv, 一个一个分析也不是不可以, 不过有一些库可以方便的给命令行添加功能, 比如yargs.
使用npm i yargs
安装其为本地版本, 在其官网上有文档, 这就是一个增强版本的解析参数的工具, 其内部操作process.argv.
app.js现在如下:
const yargs = require("yargs");
console.log(yargs.argv);
以node app.js 1 2
的方式运行, 会得到如下结果:
{ _: [ 1, 2 ], '$0': 'app.js' }
可见结果是一个对象, 键名为_的是参数的数组, 而$0固定对应的是JS文件的名称. 然后可以简单的添加指令:
yargs.version('1.1.0');
yargs.command({
command: 'add',
describe: 'Add a new note',
handler: function () {
console.log('Adding a new note!')
}
});
yargs.command({
command: 'delete',
describe: 'Delete a note',
handler: function () {
console.log('Delete a note!')
}
});
version在文档了对应的是 --version 这种选项, 而command就是对应的add delete这种参数. 对于具体的参数搭配, 可以在.command()命令中的对象添加具体的builder对象:
yargs.command({
command: 'add',
describe: 'Add a new note',
builder:{
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
}
},
handler: function (argv) {
console.log("Title: " + argv.title);
}
});
如此配置之后, 在add参数后边就可以加上 --title选项, 还可以在builder中添加多个选项. 然后handler对应的函数可以传入一个argv对象, argv对象就可以用选项名称获取选项的内容, 比如使用如下命令:
node app.js add --title="321"
显示结果就是:
Title: 321
{ _: [ 'add' ], title: '321', '$0': 'app.js' }
有了这个库之后, 就可以方便的操作一个命令+一批选项了.
用JSON存储数据
用JavaScript不用JSON, 就好比用Java不用面向对象思想一样. JSON说实在还是挺好用的. 来看看Node.js里如何使用JSON吧.
JSON对象在原版JS中就是一个内置的对象, 在Node中也是, 可以直接使用JSON.stringify()来将一个对象转换成JSON字符串, 用JSON.parse()来将一个JSON字符串转换成一个对象.
和原版JS一样, JSON只能支持字符串和数值类型的转换. 所以语法就不用说了. 这里主要是和之前介绍的写入和读取文件函数来搭配使用:
const fs = require('fs');
const game = {
name: "Baldur's Gate 3",
release: 2020
};
//从对象生成JSON字符串
const bg3JSON = JSON.stringify(game);
console.log(bg3JSON);
//写入到文件
fs.writeFileSync('data.json', bg3JSON);
//读出文件 直接读出的是一个字节数组
const content = fs.readFileSync('data.json');
//这个字节数组有一个指定编码来将其转换成字符串的方法
const dataJSON = content.toString("UTF-8");
//将JSON字符串解析成对象
const gamebg3 = JSON.parse(dataJSON);
console.log(gamebg3);
开始编写一个简单的app 通过命令行添加和删除数据
之前已经添加了yargs, 然后学会了JSON以及将JSON保存在文件中的操作, 现在就可以来编写一个简单的命令行指令, 用来记录一条条信息的应用了.
这个应用的核心就是首先通过yargs来进行配置命令与对应的参数, 之后读出文件中的JSON, 添加记录后再写回JSON文件, 就是将上边了解过的东西组合起来.
这里创建一个app.js, 然后创建一个notes.js, app.js作为主程序, 而notes.js, 将所有的与操作notes文件相关的内容都放入其中. 先来看一个最简单的添加记录的方法.
const fs = require("fs");
//添加记录的函数
const addNote = function (title, body) {
//加载文件转换而来的数组
const notes = loadNotes();
//向数组中添加一个对象
notes.push({
title: title,
body: body
});
//然后进行保存
saveNotes(notes);
};
//从文件中读出JSON并转换成对象的函数, 如果出现错误, 就返回一个空的数组
const loadNotes = function () {
try {
return JSON.parse(fs.readFileSync("notes.json").toString());
} catch (e) {
return [];
}
};
//保存notes的工具函数
const saveNotes = function (notes) {
fs.writeFileSync("notes.json", JSON.stringify(notes));
};
//导出的时候导出一个对象, addNote键对应的就是同名函数
module.exports = {
addNote: addNote
};
然后来编写app.js, 其核心思想如下:
- 先利用yargs来为add命令添加两个参数 --title 和 --body
- 然后将add命令的执行命令设置为notes.js中的添加函数
这样就可以以命令行模式执行来添加title和body, 来编写一下看看, 其实之前也基本编写的差不多了:
const notes = require("./notes");
const yargs = require("yargs");
yargs.version("0.0.1");
//注册一个add命令, 带两个选项title和body
yargs.command({
command: 'add',
describe: 'Add a new note',
builder: {
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
},
body: {
describe: 'Note body',
demandOption: true,
type: 'string'
}
},
handler: function (argv) {
notes.addNote(argv.title, argv.body)
}
});
//启动yargs的解析功能, 这一行不要忘记
yargs.parse();
编写完之后, 用如下命令:
node app.js add --title="t" --body="fdsafsda"
node app.js add --title="bg3" --body="hurry"
就可以看到生成了notes.json文件, 内容如下:
[{"title":"t","body":"fdsafsda"},{"title":"bg3","body":"hurry"}]
这里其实还有一个判断重复添加的过程, 可以规定当title相同的时候, 就算是重复, 则可以修改一下程序如下:
const addNote = function (title, body) {
const notes = loadNotes();
//检测是否有相同的内容
let isDuplicated = false;
for (let eachNote of notes) {
if (eachNote.title === title) {
isDuplicated = true;
console.log("Title: " + title + " is already taken.");
return;
}
}
notes.push({
title: title,
body: body
});
saveNotes(notes);
};
在每次添加的时候,遍历数组检查是否有相同的标题, 有就直接退出.
简单app - 查看和删除note功能
根据相同的做法,继续添加对应的指令和notes.js中的函数:
yargs.command({
command: 'remove',
describe: 'Remove a new note',
builder: {
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
},
},
handler: function (argv) {
notes.removeNote(argv.title);
}
});
yargs.command({
command: 'list',
describe: 'Remove a new note',
handler: function () {
notes.listNote()
}
});
const removeNote = function (title) {
const notes = loadNotes();
let counter = 0;
const filteredNotes = notes.filter(x => {
if (x.title === title) {
counter = counter + 1;
return false;
} else {
return true;
}
});
if (counter === 0) {
console.log("Title: " + title + "does not exists!");
} else {
saveNotes(filteredNotes);
console.log("note removed");
}
}
const listNote = function () {
const notes = loadNotes();
console.log(chalk.green('Notes List'));
for (let eachNote of notes) {
console.log(chalk.red(eachNote.title), '\t', chalk.blue(eachNote.body));
}
};
这样一个最基础的app就做好了.
用箭头函数重构
ES6一大特点就是箭头函数, 这里就用箭头函数尽量将所有的内容重构. 新的notes.js如下:
const fs = require("fs");
const chalk = require("chalk");
const addNote = (title, body) => {
const notes = loadNotes();
let isDuplicated = false;
for (let eachNote of notes) {
if (eachNote.title === title) {
isDuplicated = true;
console.log("Title: " + title + " is already taken.");
return;
}
}
notes.push({
title: title,
body: body
});
saveNotes(notes);
console.log("note added");
};
const removeNote = (title) => {
const notes = loadNotes();
let counter = 0;
const filteredNotes = notes.filter(x => {
if (x.title === title) {
counter = counter + 1;
return false;
} else {
return true;
}
});
if (counter === 0) {
console.log("Title: " + title + "does not exists!");
} else {
saveNotes(filteredNotes);
console.log("note removed");
}
}
const listNote = () => {
const notes = loadNotes();
console.log(chalk.green('Notes List'));
for (let eachNote of notes) {
console.log(chalk.red(eachNote.title), '\t', chalk.blue(eachNote.body));
}
};
const loadNotes = () => {
try {
return JSON.parse(fs.readFileSync("notes.json").toString());
} catch (e) {
return [];
}
};
const saveNotes = (notes) => {
fs.writeFileSync("notes.json", JSON.stringify(notes));
};
module.exports = {
addNote: addNote,
removeNote: removeNote,
listNote: listNote
};
新的app.js则使用ES6对象的新语法, 可以直接使用同名函数名称:
const notes = require("./notes");
const yargs = require("yargs");
yargs.version("0.0.1");
yargs.command({
command: 'add',
describe: 'Add a new note',
builder: {
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
},
body: {
describe: 'Note body',
demandOption: true,
type: 'string'
}
},
handler(argv) {
notes.addNote(argv.title, argv.body)
}
});
yargs.command({
command: 'remove',
describe: 'Remove a new note',
builder: {
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
},
},
handler(argv) {
notes.removeNote(argv.title);
}
});
yargs.command({
command: 'list',
describe: 'Remove a new note',
handler() {
notes.listNote()
}
});
yargs.parse();
查找功能
增删改查其实目前只有增删和列出功能, 改可以用先删后增来实现, 还有一个查找功能, 也很简单, 用title查找即可, 命令如下:
yargs.command({
command: 'search',
describe: 'Search a note by title',
builder: {
title: {
describe: 'Note title',
demandOption: true,
type: 'string'
},
},
handler(argv) {
notes.search(argv.title)
}
});
notes.js添加一个新函数及引出对象也添加上该函数:
const search = (title) => {
const notes = loadNotes();
const result = notes.filter(s => s.title === title);
if (result.length === 0) {
console.log("Cannot find title: " + title);
} else {
for (let eachNote of result) {
console.log(chalk.red(eachNote.title), '\t', chalk.blue(eachNote.body));
}
}
};
module.exports = {
addNote: addNote,
removeNote: removeNote,
listNote: listNote,
search:search
};
Node.js 的 debug功能
在node中debug, 有若干形式:
- 用console.log在很多地方输出变量值, 来进行debug. 太低级
- Node.js 的inspect功能, 结合Chrome
Node.js 有一个inspect功能, 对于这个app, 使用如下:
node inspect app.js
之后得到如下显示:
< Debugger listening on
< ws://127.0.0.1:9229/8c3f8ba7-388c-4c5a-b0bc-3cbd6d3b920f
< For help, see: https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in app.js:1
> 1 const notes = require("./notes");
2 const yargs = require("yargs");
3
debug>
这其实是启动了Node.js的debugger, 然后会在本地端口进行等待, 可以通过浏览器调试, 打开Chrome, 在地址栏输入 chrome://inspect, 会看到一个页面:
Remote Target
#LOCALHOST
Target (v12.18.3)
trace
app.js
file:///D:/_Coding_notes-app-1_app.js
这说明本地端口有一个可以进行inspect的内容, 点击inspect就会打开一个debugger窗口, 然后就可以在其中看到代码.
由于Chrome是V8引擎, 因此是目前唯一支持Node.js debugger的浏览器.
在其中的sources中可以在行号前打断点. 然后点靠近右上部分的箭头可以执行并且跟踪变量.