基础知识
从命令行接受输入
readline模块可以每次一行地从可读流(例如 process.stdin 流)获取输入。
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
})
readline.question(`你叫什么名字?`, name => {
console.log(`你好 ${name}!`)
readline.close()
})
Node模块规范
Node中模块用的是CommonJS规范,有以下两种使用方式:
//导出的文件中
function sum(a,b){
return a+b;
}
//方式一
exports.sum=sum;
//导出方式二
module.exports = {
sum,
...
};
//导入的文件
const sum=require("...")//文件路径
- CommonJS 以同步的方式加载模块。这一点其实可以理解,因为 Node 通常是运行于服务端,而在服务端模块文件通常存放在本地磁盘,读取速度比起前端通过网络下载要快得多,所以一般没有什么问题(但是因此也不适用于前端)
- CommonJS 输出的是模块的拷贝,这一点与 ESNext 模块不同。模块一旦输出后便独立(即后续更改不影响)
- Node 中对引入过的模块都会进行缓存,减少后续引入的开销。
nodejs常用内置模块
- fs ,文件操作模块,例如fs.readFile(异步读取文件)、fs.readFileSync(同步读取文件)
- http ,网络操作模块,用于创建服务器。如 http.createServer
- path ,路径操作模块,用于获取和处理文件路径
- url ,url 模块,主要是一些操作url的API,例如url解析
Buffer数据类型
Node.js 中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区,Buffer 类似于一个整数数组。
let buf1 = Buffer.from([97, 98, 99]); //根据一个数组创建 Buffer 对象
console.log(buf1); //<Buffer 61 62 63> 以16进制存在buffer对象中
console.log(buf1.toString());
let buf2 = Buffer.from("nodejs"); //根据一个字符串创建 Buffer 对象
console.log(buf2);
console.log(buf2.toString());
let buf3 = Buffer.alloc(10); // 创建了可以存放10个字符的buffer对象
buf3.write("abc"); //按照ASCII表的值,转16进制,存在buffer中
console.log(buf3);
console.log(buf3.toString());
//<Buffer ..... > 需要 toString() 才能看到里面的真实数据
两个文件
package.json文件
package.json 是项目描述文件,保存在项目的根目录下面,记录了当前的项目信息,用npm init -y命令生成
例子如下:
{
"name": "plant",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.26.1",
"core-js": "^3.6.5",
"echarts": "^5.3.3",
"echarts-gl": "^2.0.9",
"element-plus": "^2.1.0",
"esri-loader": "^3.4.0",
"postcss-px2rem": "^0.3.0",
"qs": "^6.10.3",
"sass": "^1.26.5",
"sass-loader": "^7.3.1",
"sass-resources-loader": "^2.2.5",
"vue": "^3.0.0",
"vue-amap": "^0.5.10",
"vue-axios": "^3.4.1",
"vue-router": "^4.0.0-0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.15",
"@vue/cli-plugin-eslint": "~4.5.15",
"@vue/cli-plugin-router": "~4.5.15",
"@vue/cli-service": "~4.5.15",
"@vue/compiler-sfc": "^3.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
version 表明了当前的版本。
name 设置了应用程序/软件包的名称。
description 是应用程序/软件包的简短描述。
main 设置了应用程序的入口点。
private 如果设置为 true,则可以防止应用程序/软件包被意外地发布到 npm。
scripts 定义了一组可以运行的 node 脚本。
dependencies 设置了作为依赖安装的 npm 软件包的列表。
devDependencies 设置了作为开发依赖安装的 npm 软件包的列表。
engines 设置了此软件包/应用程序在哪个版本的 Node.js 上运行。
browserslist 用于告知要支持哪些浏览器(及其版本)。
package-lock.json 文件
记录模块与模块之间的依赖关系
锁定包的版本
记录项目所依赖第三方包的树状结构和包的下载地址,加快重新安装的下载速度
其他
NODE_ENV是一个环境变量,NODE_ENV=“production”表示在开发环境执行代码,NODE_ENV=”development”表示在生产环境执行代码
stream pipe(流),Node中可以使用流进行数据输入输出(特别是当响应数据是大文件时,可以提高传送效率)
Node重点
事件循环
什么是事件循环?
- 事件循环类型像while循环那样,每执行一次循环体就称为一个Tick,每个 Tick 的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数,然后进入下一个循环,如果不再有事件处理,就退出进程。
- Node的事件循环机制和浏览器是不同的。
- 这些事件主要是网络请求、文件I/O等等
EventLoop
Eventloop就是一个循环的一个Tick
先上Eventloop图:┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
- timer 阶段:执行到期的setTimeout 、setInterval回调队列(所以平时定时器执行的时间有时并不准确)
- pending callback阶段:执行推迟到下一个循环迭代的I/O回调
- idle, prepare阶段:仅内部使用
- poll阶段(轮询):取出新的 I/O 事件;执行与 I/O 相关的回调(除了关闭回调,计时器调度的回调和 setImmediate 之外,几乎所有这些回调) 适当时,node 将在此处阻塞。
如果轮询队列不为空,事件循环将循环访问队列并同步执行直到空。
如果轮询队列是空的:
1.如果脚本被 setImmediate 调度,则事件循环结束轮询,进入 check(检查)阶段执行那些被调度的脚本
2.如果没有被 setImmediate 调度,则事件循环会等待回调被添加到队列中,然后立即执行
在这个过程中,一旦轮询队列为空,事件循环还会检查已经到达时间阈值(或者超时)的计时器,如果有就回到定时器阶段执行对应的回调。 - check阶段:执行setImmediate回调
- close callbacks阶段:一些关闭的回调函数
setTimeOut( )与setImmdiate( )
执行时机不同
- setImmediate 设计为在当前轮询 poll 阶段完成后执行脚本。
- setTimeout 计划在以毫秒为单位的最小阈值过去之后运行脚本。
计时器的执行顺序将根据调用它们的上下文而有所不同
- 如果两者都是主模块 (main module) 中调用的,则顺序将受到进程性能的限制(这可能会受到计算机上运行的其他应用程序的影响)。
- 如果这两个调用在一个 I/O 回调中,那么 immediate 总是执行第一。
process.nextTick()与setImmediate()
- process.nextTick 是在同一阶段立即触发,setImmediate是在事件循环接下来的阶段迭代中执行 - check 阶段。
- process.nextTick () 比 setImmediate () 触发的更快。如果想设置立即异步执行一个任务,最好不要使用 setTimeout (fn,0),而是使用 process.nextTick () 或 setImmediate ()。