学Node.js实际上就是另外一个环境的JS, 所以依然要看一看基础如何操作. 这里目的是要写一个小程序, 会用到最基本的内容, 比如命令行, JSON存储数据等.
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中可以在行号前打断点. 然后点靠近右上部分的箭头可以执行并且跟踪变量.