前沿 nodejs是一个JavaScript的跨平台解释器,目的是run JavaScript everywhere,其中的LTS是long-term support(长期支持版本)
Nodejs:nodejs
安装后可以通过以下命令进行检查:
都能在控制台中打印出版本信息即安装成功。
更换镜像源 查看当前镜像源
设置淘宝镜像源
1 node config registry https://registry.npmmirror.com/
常用的内置模块
模块名
功能
url模块
解析url请求 返回一个解析对象
path模块
用户获取文件的路径
buffer模块
处理二进制数据,可以看为一个存储二进制的固定数组
fs模块
用于操作文件(读取、写入、删除、创建)
stream模块
数据流处理,支持读取和写入流(处理大文件较为有效)
os模块
获取操作系统的信息,如类型,架构,CPU等
http模块
用于构建Web服务器,处理http请求和响应等
crypto模块
提供加密和解密的功能,支持各种hash算法,对称加密和非对称加密等
util模块
提供一些函数工具,如格式化输出
在文件夹中的终端中输入以下指令来初始化一个nodejs项目
按照要求输入一系列的配置后会生成一个package.json
文件。
1 2 3 4 5 6 7 8 9 10 11 12 { "name" : "demo" , "type" : "module" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" } , "author" : "gcnanmu" , "license" : "ISC" }
如果使用的是powerShell,会出现无法加载文件的报错,本质上是权限的原因(只有新版的nodejs会出现这个报错)
可以通过以下命令来查看当前的执行策略:
可能出现下面四种情况:
Restructed 禁止运行所有脚本
AllSigned 需要有效的数字签名
RemoteSigned 允许运行本地创建的脚本 网上来源需要数字签名
Unrestricted 允许所有运行
更改策略为RemoteSigned即可。
1 Set-ExecutionPolicy RemoteSigned
更换标准 假设此时我们想要使用url模块,我们可能会像下面这样写
1 2 3 import url from 'url' ;url.parse ('http://www.example.com?name=Tom&age=20' , true ).query ;
但是一旦runcode,就会出现如下的报错:
(node:7332) Warning: To load an ES module, set “type”: “module” in the package.json or use the .mjs extension. (Use node --trace-warnings ...
to show where the warning was created) d:\Web\drNode\demo.js:1 import url from ‘url’; ^^^^^^
SyntaxError: Cannot use import statement outside a module at internalCompileFunction (node:internal/vm:73:18) at wrapSafe (node:internal/modules/cjs/loader:1176:20) at Module._compile (node:internal/modules/cjs/loader:1218:27) at Module._extensions..js (node:internal/modules/cjs/loader:1308:10) at Module.load (node:internal/modules/cjs/loader:1117:32) at Module._load (node:internal/modules/cjs/loader:958:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:23:47
Node.js v18.15.0
这是由于不同的模块标准导致的。
JavaScript有两种模块体系:CommonJS 和ES模块(ECMAScript模块)
CommonJS 是Node.js最早采用的模块系统,使用require()
和module.exports
来导入和导出模块。文件扩展名通常是.js
1 2 3 4 5 const url = require ("url" );let urlParest = parse ('http://www.example.com?name=Tom&age=20' , true )let times = 10 ;module .exports = {urlParest,times}
ES模块 是JavaScript的官方模块系统,使用import
和export
语法。文件扩展名通常是.mjs
1 2 3 4 5 import url from "url" let urlParest = parse ('http://www.example.com?name=Tom&age=20' , true )let times = 10 ;export default {urlParest,times}
报错中给了两个解决方法:
将文件名改为.mjs
在package.json
中添加
考虑到与ES6的兼容性,建议使用第二种方法。
url模块 可以将url生成一个URL对象
1 2 const myUrl = new URL ('https://example.com:8000/hello.html?id=100&status=active' );console .log (myUrl);
得到的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 URL { href: 'https://example.com:8000/hello.html?id=100&status=active' , origin: 'https://example.com:8000' , protocol: 'https:' , username: '' , password: '' , host: 'example.com:8000' , hostname: 'example.com' , port: '8000' , pathname: '/hello.html' , search: '?id=100&status=active' , searchParams: URLSearchParams { 'id' => '100' , 'status' => 'active' }, hash : '' }
可以看到生成了一个对象,其中的属性都可以使用.
或者[]
来获取。
也可以使用parse
对url进行解析,得到的结果也是一样的(如果不使用true参数可能会显示已弃用)
1 2 3 4 5 6 7 import url from "url" ;let urlParse = url.parse ( "https://example.com:8000/hello.html?id=100&status=active" , true ); console .log (urlParse);
如果你获取本地的文件,可能会出现fileUrl
的形式(使用file:///
作为前缀),这时候使用url
模块的fileURLToPath
即可将其解析为本地文件路径链接。
1 2 3 4 console .log (import .meta .url ); let __fileName = url.fileURLToPath (import .meta .url );console .log (__fileName);
path模块 path模块的作用是对路径进行处理
首先要注意一下不同操作系统的路径规则是不同的,path模块也细分为两个领域:
win32 windows系统 可以使用path.win32
强制规定
posix Linux系统 可以使用path.posix
强制规定
实际上nodejs是会自己判断操作系统的类型,不需要自己设定
主要操作分为:
获取文件名(带扩展名) path.basename
获取所在文件夹路径 path.dirname
获取扩展名 path.extname
路径拼接 path.join
路径解析 path.parse
是否为绝对路径 path.isAbsolute
转化为绝对路径 path.basename
转化为相对路径 path.relative(参考路径,当前路径)
路径合法化 path.normalize
一些操作展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import path from "path" ;import url from "url" ;let inputPath = url.fileURLToPath (import .meta .url );let basename = path.basename (inputPath);console .log (basename); let dirname = path.dirname (inputPath);console .log (dirname); let extname = path.extname (inputPath);console .log (extname); let joinPath = path.join (dirname, basename);console .log (joinPath); let parsePath = path.parse (inputPath);console .log (parsePath);let resolvePath = path.resolve (inputPath);console .log (resolvePath); console .log (path.sep ); console .log (path.isAbsolute (inputPath)); let normalizePath = path.normalize ("/Users/username/Documents/demo///demo.js" );console .log (normalizePath); let relativePath = path.relative ( "/Users/username/Documents/demo" , "/Users/username/Documents/demo/demo.js" ); console .log (relativePath); let url1 = path.win32 .parse ("C:\\Users\\username\\Documents\\demo\\demo.js" );console .log (url1);let url2 = path.posix .parse ("/Users/username/Documents/demo/demo.js" );console .log (url2);
Buffer模块 Buffer是用来处理二进制数据(0或者1)的模块,常见的操作是将字符串转化为二进制,将二进制转化为字符串(很多API返回的都是二进制数据)。
Base64编码是一种用于将二进制数据转换为文本字符串的编码方法。它的主要作用是使二进制数据能够以文本形式表示和传输,特别是在需要通过文本协议(如HTTP、SMTP等)传输二进制数据时非常有用。二进制与base64编码相互转化也是很常见的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 console .log (Buffer .alloc (10 )); console .log (Buffer .alloc (10 , 1 )); let Name = "hello world" ;let nameBuffer = Buffer .from (Name , "utf-8" ); console .log (nameBuffer); console .log (Buffer .byteLength (nameBuffer)); console .log (nameBuffer.length ); console .log (Buffer .from (Name , "utf-8" ).toString ("utf-8" )); let base64 = Buffer .from ("hello world" ).toString ("base64" ); console .log (base64); console .log (Buffer .from (base64, "base64" ));
fs模块 在nodejs中,fs的操作模块分为两种模式:同步与异步。
例如写入文件,分为fs.writeFile
和fs.writeFileSync
两种实现方式。两者的作用是相同的,但是writeFileSync
是异步的实现方式,他不会因等待而阻塞,writeFileSync
默认提供了回调函数的实现方式,其他的操作函数也同理。
1 2 3 4 5 6 7 8 9 10 11 import fs from "fs" ;fs.mkdir ("demo/css" , { recursive : true }) fs.mkdirSync ("demo/css" , { recursive : true }, (err ) => { if (err) { console .log (err); } else { console .log ("Directory created successfully!" ); } });
JavaScript是解释性语言,他只能按从上到下的顺序一行一行执行代码,如果使用默认的回调函数是不符合日常编写程序的逻辑的 ,因此fs模块还提供了promise的实现方式,这可以使用同步的方式实现异步代码,这样程序的执行逻辑才更符合一般的编程习惯。
1 2 3 4 5 6 7 8 9 10 11 12 import fs from "fs" ;const createDir = async (dir ) => { try { await fs.promises .mkdir (dir, { recursive : true }); console .log ("Directory created successfully!" ); } catch (error) { console .log (error); } }; createDir ("demo/js" );
因此更加推荐使用async的方法。
fs模块有以下的常见操作:
mkdir 创建目录,如果想要创建多级目录设置{recursive:true}
writeFile 覆盖写入 会覆盖之前的内容
appendFile 追加写入
readFile 读取文件,返回的是buffer对象,需要使用指定编码进行转化
access 判断文件或者目录是否存在
stat 获取文件或者目录的详细信息,返回一个对象
rename 目录或者文件重命名
unlink 删除文件 不存在会报错
rmdir 删除文件夹 不存在则报错
readdir 获取当前文件夹下的目录或者文件
一些操作的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 import fs, { Dir , Dirent } from "fs" ;const createDir = async (dir ) => { try { await fs.promises .mkdir (dir, { recursive : true }); console .log ("Directory created successfully!" ); } catch (error) { console .log (error); } }; const writeFile = async (file, data ) => { try { await fs.promises .writeFile (file, data); console .log ("File written successfully!" ); } catch (error) { console .log (error); } }; const appendFile = async (file, data ) => { try { await fs.promises .appendFile (file, data); console .log ("Data appended successfully!" ); } catch (error) { console .log (error); } }; const readFile = async (file ) => { try { const data = await fs.promises .readFile (file, { encoding : "utf-8" }); console .log (data); } catch (error) { console .log (error); } }; const checkDir = async (dir ) => { try { await fs.promises .access (dir, fs.promises .constants .F_OK ); console .log ("Directory exists!" ); } catch (error) { console .log ("Directory does not exist!" ); } }; const getFileInfo = async (file ) => { try { const stats = await fs.promises .stat (file); if (stats.isFile ()) { console .log (`${file} is a file` ); } if (stats.isDirectory ()) { console .log (`${file} is a directory` ); } } catch (error) { console .log (error); } }; const renameFile = async (oldPath, newPath ) => { try { await fs.promises .rename (oldPath, newPath); console .log ("File renamed successfully!" ); } catch (error) { console .log (error); } }; const deleteFile = async (file ) => { try { await fs.promises .unlink (file); console .log ("File deleted successfully!" ); } catch (error) { console .log (error); } }; const deleteDir = async (dir ) => { try { await fs.promises .rmdir (dir, { recursive : true }); console .log ("Directory deleted successfully!" ); } catch (error) { console .log (error); } }; const readDir = async (dir ) => { try { const files = await fs.promises .readdir (dir, { recursive : true }); console .log (files); } catch (error) { console .log (error); } }; const readDir2 = async (dir ) => { try { const files = await fs.promises .opendir (dir); for await (const file of files) { console .log (file.name ); } } catch (error) { console .log (error); } }; readDir2 ("demo" );const readDir3 = async (dir ) => { try { const files = await fs.promises .opendir (dir); for await (const file of files) { if (file.isDirectory ()) { console .log (`Directory: ${file.name} ` ); readDir3 (`${dir} /${file.name} ` ); } else { console .log (`File: ${file.name} ` ); } } } catch (error) { console .log (error); } } readDir3 ("demo" );
流式操作 stream的作用是将数据按照设定好的大小一块一块的读取,而不是整个读入,这样可以节省内存,降低服务压力。
对于读取流([fs.createReadStream
](vscode-file://vscode-app/e:/microsoft vs code/resources/app/out/vs/code/electron-sandbox/workbench/workbench-apc-extension.html)),默认缓冲区大小为64KB(65536字节)。
对于写入流([fs.createWriteStream
](vscode-file://vscode-app/e:/microsoft vs code/resources/app/out/vs/code/electron-sandbox/workbench/workbench-apc-extension.html)),默认缓冲区大小也是16KB(16384字节)
可以通过单独设置highWaterMark
属性来设定最大的模块大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import fs from "fs" ;const writefile = async (path, data ) => { try { await fs.promises .writeFile (path, data); console .log ("数据写入文件成功" ); } catch (error) { console .error (error); } }; const writeFileStr = (path, data ) => { const writeStream = fs.createWriteStream (path, { highWaterMark : 1024 * 12 }); writeStream.on ("error" , (error ) => { console .log (error); }); writeStream.on ("finish" , () => { console .log ("覆盖写入成功" ); }); writeStream.write (data, "utf-8" ); writeStream.end (); }; const appendFileStr = (path, data ) => { const appendFileStream = fs.createWriteStream (path, { flags : "a" }); appendFileStream.on ("error" , (error ) => { console .log (error); }); appendFileStream.on ("finish" , () => { console .log ("追加写入文件成功" ); }); appendFileStream.write (data, "utf-8" ); appendFileStream.end (); }; const readFileStr = (path ) => { const readFileStream = fs.createReadStream (path); let content = "" ; readFileStream.on ("data" , (chunk ) => { console .log (chunk); content += chunk.toString ("utf8" ); }); readFileStream.on ("end" , () => { console .log (content); }); readFileStream.on ("error" , (error ) => { console .log (error); }); }; readFileStr ("demo/js/hello2.txt" );
OS模块 OS模块常用于获取系统信息,常见的系统信息如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import os from "os" console .log ("内核版本:" , os.version ());console .log ("系统类型:" , os.type ());console .log ("系统架构:" , os.arch ());console .log ("主机名:" , os.hostname ());console .log ("系统平台:" , os.platform ());console .log ("系统发行版本:" , os.release ());console .log ("CPU核心数:" , os.cpus ().length );console .log ("内存信息:" , os.totalmem ());console .log ("空闲内存:" , os.freemem ());console .log ("用户目录:" , os.homedir ());console .log ("用户信息:" , os.userInfo ());console .log ("临时目录:" , os.tmpdir ());console .log ("系统运行时间:" , os.uptime ());
crypto模块 提供了加密和解密的功能
常见的加密算法有MD5,SHA_1,AES-GCM等,分为对称加密和非对称加密。对称与否在于解密时使用的密匙和加密是否相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 import crypto from "crypto" ;const md5 = (data ) => { const hash = crypto.createHash ("md5" ); hash.update (data); return hash.digest ("hex" ); }; const sha1 = (data ) => { const hash = crypto.createHash ("sha1" ); hash.update (data); return hash.digest ("hex" ); }; console .log ("md5:" , md5 ("Hello, world!" )); console .log ("sha1:" , sha1 ("Hello, world!" )); const randomBytes = (size ) => { let newSize = Math .round (size / 2 ); let buffer = crypto.randomBytes (newSize); let hexStr = buffer.toString ("hex" ); return hexStr.slice (0 , size); }; console .log ("随机的秘钥:" , randomBytes (16 ));const generateAESKey = ( ) => { return crypto.randomBytes (32 ); }; const aesEncrypt = (data, key ) => { const nonce = crypto.randomBytes (12 ); const cipher = crypto.createCipheriv ("aes-256-gcm" , key, nonce); let encrypted = cipher.update (data, "utf8" , "hex" ); encrypted += cipher.final ("hex" ); const authTag = cipher.getAuthTag ().toString ("hex" ); const encryJson = JSON .stringify ({ nonce : nonce.toString ("base64" ), encrypted : encrypted, authTag : authTag, }); return encryJson; }; const aesDecrypt = (encrypted, key ) => { const encryJson = JSON .parse (encrypted); const nonce = Buffer .from (encryJson.nonce , "base64" ); const encryptedData = encryJson.encrypted ; const authTag = Buffer .from (encryJson.authTag , "hex" ); const decipher = crypto.createDecipheriv ("aes-256-gcm" , key, nonce); decipher.setAuthTag (authTag); let decrypted = decipher.update (encryptedData, "hex" , "utf8" ); decrypted += decipher.final ("utf8" ); return decrypted; }; const key = generateAESKey ();const encrypted = aesEncrypt ("Hello, world!" , key);console .log ("encrypted:" , encrypted); console .log ("decrypted:" , aesDecrypt (encrypted, key));
nodemon nodemon用来监视node.js应用程序中的任何更改并自动重启服务,其中mon是monitor(监视)的缩写。
安装nodemon
其中涉及到全局安装和局部安装的区别:
全局安装可以在任何目录下使用
局部安装只能在当前目录下使用,均被安装到目录下的node_modules
文件夹中
在命令行中通过以下命令进行使用
如果想要在项目中配置npm run dev
的方法运行nodemon filename.js
,可以在package.json的script
中添加如下的配置项:
1 2 3 4 "scripts" : { "dev" : "nodemon index.js" , "test" : "echo \"Error: no test specified\" && exit 1" } ,
之后可以通过npm run dev
运行设定好的nodemon index.js
命令
http模块 http模块是一个用于创建服务器与发送请求的模块,可以通过创建server来对所有的请求作出回应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import http from "http" ;const port = 8000 ;const hostname = "127.0.0.1" ;const server = http.createServer ((req, res ) => { res.writeHead (200 , { "Content-Type" : "text/plain" }); res.end ("Hello World\n" ); }); server.listen (port, hostname, () => { console .log (`server is running on http://${hostname} :${port} ` ); });
以上程序通过http模块创建了一个server对象,这个对象会一直监听设定好的端口号,一旦有requset请求被发送到服务器,则按照写好的逻辑对请求进行回应。回应部分使用的是res.end()
返回具体的响应体,如有必要,可以通过res.writeHead()
设置请求头。使用npm run dev
运行程序后,打开127.0.0.1:8000
可以看到页面显示Hello World。
获取请求数据 在生产实际中,往往需要先根据请求所发送的信息来返回处理后的数据。
在server对象中,使用req来作为请求对象,可以获取req的属性(例如请求方式,请求体,请求头)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import http from "http" ;const port = 8000 ;const hostname = "127.0.0.1" ;const server = http.createServer ((req, res ) => { console .log (`${req.method} ${req.url} ` ); console .log (req.headers .referer ); console .log (req.headers ["user-agent" ]); res.statusCode = 200 ; res.setHeader ("Content-Type" , "text/html" , "charset=utf-8" ); res.write ("<h1>Hello World</h1>" ); res.end (); }); server.listen (port, hostname, () => { console .log (`server is running on http://${hostname} :${port} ` ); });
访问127.0.0.1:8000
后控制台得到的打印结果如下:
1 2 3 4 5 6 GET / undefined Mozilla/5.0 (Windows NT 10.0 ; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0 .0.0 Safari/537.36 Edg/127.0 .0.0 GET /favicon.ico http://127.0 .0.1 :8000 / Mozilla/5.0 (Windows NT 10.0 ; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0 .0.0 Safari/537.36 Edg/127.0
得到两次重复的打印结果,通过观察可以知道,这是因为每次请求的同时都会默认对网站图标进行请求。如果想要去掉这个请求可以通过req.url
属性进行条件判断。
1 2 3 4 5 6 7 const server = http.createServer ((req, res ) => { if (req.url == "/favicon.ico" ) { return ; } }
这样就可以只得到一次的打印结果
使用URL获取请求参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import http from "http" ;const port = 8000 ;const hostname = "127.0.0.1" ;const server = http.createServer ((req, res ) => { if (req.url === "/favicon.ico" ) { return ; } let fullurl = `http://${hostname} :${port} ${req.url} ` ; console .log (`fullurl:${fullurl} ` ); let urlObj = new URL (fullurl); const queryObj = new URLSearchParams (urlObj.search ); console .log (queryObj); console .log ("id:" , queryObj.get ("id" )); console .log ("web:" , queryObj.get ("web" )); res.writeHead (200 , { "Content-Type" : "Application/json" , charset : "utf-8" , }); let result = { id : queryObj.get ("id" ), web : queryObj.get ("web" ), }; res.end (JSON .stringify (result)); }); server.listen (port, hostname, () => { console .log (`server is running on http://${hostname} :${port} ` ); });
使用npm run dev
可以在控制台看到整个URL对象的属性信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 URL { href: 'http://127.0.0.1:8000/?id=1&web=baidu.com' , origin: 'http://127.0.0.1:8000' , protocol: 'http:' , username: '' , password: '' , host: '127.0.0.1:8000' , hostname: '127.0.0.1' , port: '8000' , pathname: '/' , search: '?id=1&web=baidu.com' , searchParams: URLSearchParams { 'id' => '1' , 'web' => 'baidu.com' }, hash: '' }
一般来说,我们需要的是searchParams
这个属性值
有两种处理方法:
使用urlObj.searchParams
获取
使用new URLSearchParams(urlObj.search)
获取
之后可以在网页中看到如下信息:
1 2 3 4 { "id" : "1" , "web" : "baidu.com" }
readline模块 是一个控制台的模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import { createInterface } from "readline" ;import fs from "fs" ;const rl = createInterface ({ input : process.stdin , output : process.stdout , }); rl.question ("What do you think of Node.js? " , (answer ) => { if ( answer === "Yes" || answer === "yes" || answer == "y" || answer == "Y" ) { console .log ("You are right!" ); } else { console .log ("You are wrong!" ); } console .log (`Thank you for your valuable feedback: ${answer} ` ); rl.close (); }); rl.on ("close" , () => { console .log ("Readline interface closed!" ); process.exit (0 ); });
通过上述的代码,可以看到如下的输出:
1 2 3 4 5 6 7 8 9 10 (base) PS D:\Web\drNode> node "d:\Web\drNode\tempCodeRunnerFile.js" What do you think of Node.js? yes You are right! Thank you for your valuable feedback: yes Readline interface closed! (base) PS D:\Web\drNode> node "d:\Web\drNode\tempCodeRunnerFile.js" What do you think of Node.js? no You are wrong! Thank you for your valuable feedback: no Readline interface closed!