Node.js
1. Node.js 是什么
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。其底层由 C/C++实现。
2. Node.js 的特点
优点:
- 异步非阻塞的 I/O(I/O 线程池) I/O 操作分别对应读写操作,线程池指的是将多个线程放在一块内存空间中等候调用。
- 特别适用于 I/O 密集型应用 cpu 密集型:要进行大量的计算,消耗 CPU 资源; I/O 密集型:CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成。
- 事件循环机制(与浏览器的不一样)
- 单线程(成也单线程,败也单线程)
- 跨平台
不足:
- 回调函数嵌套太多、太深(俗称回调地狱)
- 单线程,处理不好 CPU 密集型任务
3. Node 中的函数
Node 中任何一个模块(js 文件)都被一个外层函数所包裹:
function (exports, require, module, **filename, **dirname) {}
这个函数是所有模块都有的,node 编译时往其中注入 5 个参数:
exports
暴露模块require
引入模块module
exports 属性暴露模块__filename
文件的绝对路径__dirname
文件夹的绝对路径
为什么要设计这个外层函数(这个外层函数有什么作用?)
- 用于支持模块化语法
- 隐藏服务器内部实现(从作用域角度去看)
查看这个外层函数的方法:
console.log(arguments.callee.toString()); //输出外层函数
4. Node 中的 global
Node 端,js 的组成:
- 没有了 BOM ---> 因为服务器不需要(服务端没有浏览器对象)
- 没有了 DOM ---> 因为没有浏览器窗口
- 几乎包含了所有的 ES 规范
- 没有了 window,但是取而代之的是一个叫做 global 的全局变量。
global 的一些常用属性:
clearImmediate
: 清空立即执行函数clearInterval
: 清除循环定时器clearTimeout
: 清除延迟定时器setImmediate
: 设置立即执行函数setInterval
: 设置循环定时器setTimeout
: 设置延迟定时器
在 Node 中禁止函数的 this 指向 global,而是指向了一个空对象
console.log(this); // {}
console.log(global);
5. Node 的事件循环模型
第一个阶段:timers(定时器阶段--setTimeout,setInterval)
- 开始计时
- 执行定时器的回调
第二个阶段:pending callbacks (系统阶段)
第三个阶段:idle, prepare (准备阶段)
第四个阶段:poll(轮询阶段,核心)
- 如果回调队列里有待执行的回调函数(除 setTimeout,setInterval,setImmediate 之外) 从回调队列中取出回调函数,同步执行(一个一个执行),直到回调队列为空了,或者达到系统最大限度。
- 如果回调队列为空
- 如果有设置过 setImmediate 进入下一个 check 阶段,目的:为了执行 setImmediate 所设置的回调。
- 如果未设置过 setImmediate 在此阶段停留,等待回调函数被插入回调队列。 若定时器到点了,进入下一个 check 阶段,原因:为了走第五阶段,随后走第六阶段,随后第一阶段(最终目的)
第五个阶段:check (专门用于执行 setImmediate 所设置的回调)
第六个阶段:close callbacks (关闭回调阶段)
process.nextTick()
用于设置立即执行函数("VIP" ----- 能在任意阶段优先执行,但不能快过主线程)
6. 包与 npm 包管理器
什么是包? 我们电脑上的文件夹,包含了某些特定的文件,符合了某些特定的结构,就是一个包。
一个标准的包,应该包含哪些内容?
package.json
------- 描述文件(包的 “说明书”,必须要有!!!)bin
-----------------可执行二进制文件lib
---------------- 经过编译后的 js 代码doc
---------------- 文档(说明文档、bug 修复文档、版本变更记录文档)test
--------------- 一些测试报告
如何让一个普通文件夹变成一个包? 让这个文件夹拥有一个:
package.json
文件即可,且package.json
里面的内容要合法。 执行命令:npm init
包名的要求:不能有中文、不能有大写字母、同时尽量不要以数字开头、不能与 npm 仓库上其他包同名。npm 与 node 的关系?(npm:node package manager) 安装 node 后自动安装 npm(npm 是 node 官方出的包管理器,专门用于管理包)
npm 的常用命令?
一、【搜索】:
npm search xxxxx
- 通过网址搜索:https://www.npmjs.com
二、【安装】:(安装之前必须保证文件夹内有 package.json,且里面的内容格式合法)
npm install xxxxx --save
或npm i xxxx -S
或npm i xxxx
备注:
- 局部安装完的第三方包,放在当前目录中 node_modules 这个文件夹里
- 安装完毕会自动产生一个 package-lock.json(npm 版本在 5 以后才有),里面缓存的是每个下载过的包的地址,目的是下次安装时速度快一些。
- 当安装完一个包,该包的名字会自动写入到 package.json 中的【dependencies(生产依赖)】里。npm5 及之前版本要加上--save 后缀才可以。
npm install xxxxx --save-dev
或npm i xxxxx -D
安装包并将该包写入到【devDependencies(开发依赖中)】备注:什么是生产依赖与开发依赖?
- 只在开发时(写代码时)时才用到的库,就是开发依赖 ----- 例如:语法检查、压缩代码、扩展 css 前缀的包。
- 在生产环境中(项目上线)不可缺少的,就是生产依赖 ------ 例如:jquery、bootStrap 等等。
- 注意:某些包即属于开发依赖,又属于生产依赖 -------例如:jquery。
npm i xxxx -g
全局安装 xxxx 包(一般来说,带有指令集的包要进行全局安装,例如:browserify、babel 等) 全局安装的包,其指令到处可用,如果该包不带有指令,就无需全局安装。 查看全局安装的位置:npm root -gnpm i xxx@yyy
:安装 xxx 包的 yyy 版本npm i
:安装 package.json 中声明的所有包
三、【移除】:
npm remove xxxxx
在 node_module 中删除 xxxx 包,同时会删除该包在 package.json 中的声明四、【其他命令】:
npm aduit fix
:检测项目依赖中的一些问题,并且尝试着修复。npm view xxxxx versions
:查看远程 npm 仓库中 xxxx 包的所有版本信息npm view xxxxx version
:查看 npm 仓库中 xxxx 包的最新版本npm ls xxxx
:查看我们所安装的 xxxx 包的版本
五、【关于版本 号的说明】:
- "^3.x.x" :锁定大版本,以后安装包的时候,保证包是 3.x.x 版本,x 默认取最新的。
- "~3.1.x" :锁定小版本,以后安装包的时候,保证包是 3.1.x 版本,x 默认取最新的。
- "3.1.1" :锁定完整版本,以后安装包的时候,保证包必须是 3.1.1 版本。
7. Buffer 缓冲器
Buffer 是什么:
- 它是一个【类似于数组】的对象,用于存储数据(存储的是二进制数据)。
- Buffer 的效率很高,存储和读取很快,它是直接对计算机的内存进行操作。
- Buffer 的大小一旦确定了,不可修改。
- 每个元素占用内存的大小为 1 字节。
- Buffer 是 Node 中的非常核心的模块,无需下载、无需引入,直接即可使用。
8. Node 中的文件操作
Node 中的文件系统:
- 在 NodeJs 中有一个文件系统,所谓的文件系统,就是对计算机中的文件进行增删改查等操作。
- 在 NodeJs 中,给我们提供了一个模块,叫做 fs 模块(文件系统),专门用于操作文件。
- fs 模块是 Node 的核心模块,使用的时候,无需下载,直接引入。
异步文件写入 (简单文件写入)
fs.writeFile(file, data[, options], callback)
// --file:要写入的文件路径+文件名+后缀
// --data:要写入的数据
// --options:配置对象(可选参数)
// --encoding:设置文件的编码方式,默认值:utf8(万国码)
// --mode:设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
// --0o111:文件可被执行的权限 .exe .msc 几乎不用,linux有自己一套操作方法。
// --0o222:文件可被写入的权限
// --0o444:文件可被读取的权限
// --flag:打开文件要执行的操作,默认值是'w'
// --a :追加
// --w :写入
// --callback:回调函数
// --err:错误对象
在 Node 中有这样一个原则:错误优先。
- 创建一个可写流
fs.createWriteStream(path[, options])
// --path:要写入文件的路径+文件名+文件后缀
// --options:配置对象(可选参数)
// --flags:打开文件要执行的操作,默认值是'w'
// --encoding :设置文件的编码方式,默认值:utf8(万国码)
// --fd : 文件统一标识符,linux下文件标识符
// --mode :设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
// --autoClose : 自动关闭 --- 文件,默认值:true
// --emitClose : 关闭 --- 文件,默认值:false
// --start : 写入文件的起始位置(偏移量)
// 创建可写流案例:
let fs = require('fs')
//创建一个可写流----水管到位了
let ws = fs.createWriteStream(__dirname+'/demo.txt',{start:10})
//只要用到了流,就必须监测流的状态
ws.on('open',function () {
console.log('可写流打开了')
})
ws.on('close',function () {
console.log('可写流关闭了')
})
//使用可写流写入数据
ws.write('美女\n')
ws.write('霉女?\n')
ws.write('到底是哪一个?\n')
//ws.close() //如果在Node的8版本中,使用此方法关闭流会造成数据丢失
ws.end() //在Node的8版本中,要用end方法关闭流
- 简单文件读取
fs.readFile(path[, options], callback)
// --path:要读取文件的路径+文件名+后缀
// --options:配置对象(可选)
// --callback:回调
// --err:错误对象
// --data:读取出来的数据
- 流式文件读取
fs.createReadStream(path[, options])
// --path:要读取的文件路径+文件名+后缀
// --options:
// --flags:打开文件要执行的操作,默认值是'w'
// --encoding:设置文件的编码方式,默认值:utf8(万国码)
// --fd: 文件统一标识符,linux下文件标识符
// --mode:设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
// --autoClose: 自动关闭 --- 文件,默认值:true
// --emitClose: 强制关闭 --- 文件,默认值:false
// --start :起始偏移量
// --end : 结束偏移量
// --highWaterMark:每次读取数据的大小,默认值是64 * 1024
let { createReadStream, createWriteStream } = require("fs");
//创建可读流案例
let rs = createReadStream(__dirname + "/music.mp3", {
highWaterMark: 10 * 1024 * 1024,
//start:60000,
//end:120000
});
//创建一个可写流
let ws = createWriteStream("../haha.mp3");
//只要用到了流,就必须监测流的状态
rs.on("open", function () {
console.log("可读流打开了");
});
//可读流读取完毕后会自动关闭
rs.on("close", function () {
console.log("可读流关闭了");
ws.close(); // 在可读流关闭的时候关闭可写流
});
ws.on("open", function () {
console.log("可写流打开了");
});
ws.on("close", function () {
console.log("可写流关闭了");
});
//给可读流绑定一个data事件,就会触发可读流自动读取内容。
rs.on("data", function (data) {
//Buffer实例的length属性,是表示该Buffer实例占用内存空间的大小
console.log(data.length); //输出的是65536,每次读取64KB的内容
ws.write(data);
//ws.close() //若在此处关闭流,会写入一次,后续数据丢失
});
//ws.close() //若在此处关闭流,导致无法写入数据(在主线程上)