一、Node.js是什么?和浏览器端JS有什么区别?
Node.js 是一个基于Chrome V8引擎的Javascript运行环境。使用了事件驱动,非阻塞式I/O模型。用于服务端的开发,例如数据库的访问,其它服务器的调用。
优点及适用场景:
- 处理高并发场景性能更佳。
- Node.js擅长任务调度,善于I/O,不善于计算。
- 与webSocket配合,开发长连接的实时交互应用程序。
缺点
- 不适合CPU密集应用
- 可靠性不高,一旦代码某个环节崩溃,整个系统都崩溃。
异同:
- 浏览器和 Node.js 均使用 JavaScript 作为其编程语言
- 在浏览器中,大多数时候做的是与 DOM 或其??他 Web 平台 API(例如 Cookies)进行交互。 Node.js 中不存在这些,也没有?
document 、window 、等其他的对象。 - Node.js 版本支持的所有现代的 ES6-7-8-9 JavaScript
- Node.js 使用 CommonJS 模块系统,而在浏览器中,使用 ES 模块标准。
- Node.js 中使用?
require() ,而在浏览器中则使用?import
二、Node.js三大特点
* 三个特点是相辅相成的,少一个都不行。
(1)单线程
- Java、PHP等服务端语言中,会为每一个客户端创建一个新的线程;而Node.js仅仅使用一个线程。
- 配合异步非阻塞I/O、事件驱动机制,让node.js程序上宏观上也是并行的。
- 好处:不再有线程的创建、销毁的时间开销
- 坏处:一个用户把线程搞崩了,整个服务就崩了
(2)异步非阻塞I/O
- 当某个I/O执行完毕时,以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数。
- 为了处理异步I/O,线程必须有事件循环,不断地检查有没有未处理事件,依次予以处理。
什么是同步 vs 异步、阻塞vs非阻塞?
同步和异步关注的是消息通信机制。
同步是指,在发出一个调用时,在没有得到结果之前,该调用就不返回。但一旦调用返回,就得到返回值了。
异步则相反,调用发出之后,调用者不会立刻得到结果,而是在调用发出之后,被调用者通过状态、通知来通知调用者,或者通过回调函数处理这个调用。
阻塞和非阻塞关注的是程序在等待调用结果(消息、返回值)时的状态。
阻塞调用指的是调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指的是不能立刻得到结果之前,该调用不会阻塞当前线程。
场景举例:
同步阻塞:王大爷把水壶放到火上烧,然后啥也不干在那等,直到水开了他再去做别的事情。
同步非阻塞:王大爷把水壶放到火上之后就去看电视,时不时来瞅一眼水有没有开。
异步阻塞:王大爷去买了个响水壶,他把响水壶放在火上烧,然后也是等着水开,水开的时候水壶会发出声响。
异步非阻塞:王大爷把响水壶放在火上,然后就去看电视,这时他不用时不时来看一眼,因为水开的时候,响水壶会发出声音通知王大爷。
阻塞和非阻塞说明的是王大爷的状态;同步异步说明的是水壶的调用姿势。
水壶能在水烧好的时候主动响起,等同于异步能在结束时通知主线程并且回调,所以,异步一般配合非阻塞,能更好发挥其作用。
(3)事件驱动
http://nodejs.cn/learn/the-nodejs-event-loop
三、Node.js安装 vs 通过nvm管理多个版本的Node.js
一般在官网下载最新稳定版本安装包即可。但是,当不同项目使用不同版本的node.js时,就会有点麻烦了。我们可以通过nvm来管理不同版本的Node.js。
(1) 下载nvm-windows工具:https://github.com/coreybutler/nvm-windows/releases
(2)解压并安装下载好的nvm-setup.exe
(3) ?nvm? 查看是否安装好 nvm
??? nvm list available? 查看 各node.js版本
??? nvm install 14.17.3? 安装指定版本node.js
??? nvm use 14.17.3??? 使用指定版本node.js
(4) 坑点:nvm 安装了指定版本的node,但是却没有npm!!! 安装目录下的node_modules里啥也没有!!
(5) 解决方案:去node.js官网上下载对应的安装包,把node_modules目录下的内容原封不动地复制到通过nvm安装的对应版本的node.js下的node_modules目录中。把npm,npm.cmd,npx,npx.cmd 四个文件复制到node_modules同级目录中。
四、什么是模块化?
每个模块都是一个独立的功能体;
每个文件都是一个模块,有自己的作用域;
在一个文件里定义的变量,函数,类都是私有的,对其它文件不可见。
可以分为自定义模块、核心模块(内置模块)、第三方模块。
五、自定义模块
module.exports 导出,其它文件通过 require引入
a.js
module.exports = {
sayHi () {
console.log("hello")
},
add (x, y) {
return x + y
},
aa:"this is a.js"
}
b.js
const { aa, sayHi, add } = require("./a")
console.log(aa)
sayHi()
console.log(add(3,4))
六、Node.js常用内置模块
①fs模块:操作文件相关
- 操作文件API(?*前者是异步操作,后者加Sync的是同步操作,阻塞线程。)
- readFile/readFileSync? 读取文件内容
- writeFile/writeFileSync? 写入文件内容
- rename/renameSync??? 修改文件名、目录
- unlink/unlinkSync? 删除文件
- copyFile/copyFileSync? 复制文件
- mkdir/mkdirSync? 创建目录
- readdir/readdirSync? 读取目录
- rmdir / rmdirSync 删除非空目录
- stat / statSync? 获取文件或者目录的详细信息,返回一个stat对象
? ? ? ? ? ? ? ? ? ?stat.isFile()? 是否是一个文件
? ? ? ? ? ? ? ? ? ?stat. isDirectory() 是否是一个目录
?
// 1、引入核心模块 fs 文件及目录的增删改查
const fs = require("fs")
/**
* 所有文件操作,没有加async,都是异步操作。
* 1-1、文件写入
* 参数1:文件名 string
* 参数2:要写入文件的内容 string
* 参数3(可选):{flag:"a"/"w"/"r"} 追加/写入/读取
*/
fs.writeFile("test.txt", "要写入文件中的内容", { flag: "w" }, (err) => {
if (err) {
return console.log(err)
}
console.log("写入成功");
})
/**
* 1-2、文件读取
* 参数1:文件名
* 参数2(可选):编码格式
* 参数3、回调
*/
fs.readFile("index.js", "utf-8", (err, data) => {
if (err) {
return console.log(err)
}
console.log(data.toString());
})
/**
* 1-3、修改文件名
*/
fs.rename("test.txt", "demo.txt", err => {
if (err) {
return console.log(err)
}
console.log("修改成功");
})
/**
* 1-4、删除文件
*/
fs.unlink("test.txt", err => {
if (err) return console.log(err)
console.log("删除成功");
})
/**1-5、复制文件:先读取再写入 */
fs.copyFile("demo.txt", "copy.txt", err => {
if (err) return console.log(err)
console.log("复制成功");
})
/**2-1、创建目录 */
fs.mkdir("testDir", err => {
if (err) return console.log(err)
console.log("创建成功");
})
/**2-2、修改目录 */
fs.rename("testDir","modifyDir" ,err => {
if (err) return console.log(err)
console.log("修改成功");
})
/**2-3、读取目录 */
fs.readdir("modifyDir", (err,data) => {
if (err) return console.log(err)
console.log(data) // 返回的是一个数组:[ 'modify.html', 'ttttt.txt' ]
})
/**2-4、删除目录(删除空目录) */
fs.rmdir("emptyDir", err => {
if (err) return console.log(err)
console.log("删除成功");
})
/**2-5、判断文件或者目录是否存在 */
fs.exists("modifyDir", exists => {
console.log(exists); // 返回一个布尔值
})
/**2-6、获取文件或者目录的详细信息 */
fs.stat("index.js", (err, stat) => {
if (err) return console.log(err)
console.log(stat)
// 是否是一个文件
let res1 = stat.isFile()
// 是否是一个目录
let res2 = stat.isDirectory()
console.log(res1,res2);
})
/**2-7、删除非空文件夹
* 思路:递归查找每一级,删除其下所有文件,最后删除空文件夹
*/
function removeDir (path) {
const data = fs.readdirSync(path);
for (let i = 0; i < data.length; i++) {
const url = path +"/"+ data[i]
const stat = fs.statSync(url)
if (stat.isDirectory()) {
removeDir(url)
} else {
fs.unlinkSync(url)
}
}
fs.rmdirSync(path)
}
removeDir("modifyDir")
? ? ? 2.buffer类(处理二进制数据)
? ? ? ? ? ?计算机中所有内容都以二进制存储
? ? ? ? ? ?JS中没有描述二进制的变量
? ? ? ? ??
// buffer node中的一个类
// 1、通过alloc创建指定大小buffer
let buffer = Buffer.alloc(10) // <Buffer 00 00 00 00 00 00 00 00 00 00>
console.log(buffer);
// 2、通过字符串的形式创建
let buffer = Buffer.from('大家好') // <Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>
console.log(buffer);
// 3、通过数组形式创建
let buffer = Buffer.from([0xe5 ,0xa4, 0xa7, 0xe5, 0xae, 0xb6, 0xe5, 0xa5, 0xbd])
console.log(buffer); // <Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>
// 4、buffer转字符串
console.log(buffer.toString());
// 5、乱码问题
let buffer1 = Buffer.from([0xe5, 0xa4, 0xa7, 0xe5])
let buffer2 = Buffer.from([0xae, 0xb6, 0xe5, 0xa5, 0xbd])
console.log(buffer1.toString()); // 乱码了
// 6、合并buffer
let buffer3 = Buffer.concat([buffer1, buffer2])
console.log(buffer3.toString());
// 7、使用string decoder 解决乱码问题
let { StringDecoder } = require("string_decoder")
let decoder = new StringDecoder()
let res1 = decoder.write(buffer1)
let res2 = decoder.write(buffer2)
console.log(res1); // 大
console.log(res2); // 家好
? ? ? ?3.stream 流
流是一种以高效的方式处理读/写文件、网络通信、或任何类型的端到端的信息交换。
在传统的方式中,当告诉程序读取文件时,这会将文件从头到尾读入内存,然后进行处理。使用流,则可以逐个片段地读取并处理(而无需全部保存在内存中)。
const?fs?=?require("fs")
const?rs?=?fs.createReadStream("./001.jpg")
const?ws?=?fs.createWriteStream("./copy.jpg")
rs.pipe(ws)
②url模块
③querystring模块
// 引入URL模块
const url = require("url")
// 引入查询字符串模块
const qs = require("querystring")
// 声明一个URL
const urlstring = "https://www.baidu.com:443/index.html?c=12&d=13#sadfsadf";
// 将URL解析成对象
const obj = url.parse(urlstring)
console.log(obj);
/**
* Url {
protocol: 'https:',
slashes: true,
auth: null,
host: 'www.baidu.com:443',
port: '443',
hostname: 'www.baidu.com',
hash: '#sadfsadf',
search: '?c=12&d=13',
query: 'c=12&d=13',
pathname: '/index.html',
path: '/index.html?c=12&d=13',
href: 'https://www.baidu.com:443/index.html?c=12&d=13#sadfsadf'
}
*/
console.log(obj.query) // c=12&d=13
// 将查询字符串转换为对象
let resultObj = qs.parse(obj.query)
console.log(resultObj); // { c: '12', d: '13' }
④path模块
__dirname: 获取当前模块的绝对路径。
__filename: 获取当前模块的绝对路径和模块名称
path.basename():获取文件名部分
path.extname():获取文件的父文件夹
path.dirname():获取文件的扩展名
path.join():连接路径的两个或多个片段
⑤http模块
// 1、引入内置http模块 (这个模块提供了http服务)
const http = require('http')
// 2、创建服务器,是个异步函数
const sever = http.createServer((req, res) => {
// req 表示 request 用户的请求
// res 表示 response 服务器的相应
// 3、 设置响应报文
res.setHeader("Content-Type", "text/html; charset=UTF8")
// 4、写入内容
res.write("<h1>Hello Node.js</h1>")
res.write("<ul>")
res.write("<li>Express</li>")
res.write("<li>NPM</li>")
res.write("<li>Koa</li>")
res.write("</ul>")
// 5、结束并发送响应 如果本次操作不结束,浏览器则一直显示载入中
res.end()
})
// 6、设置监听端口
sever.listen(3000)
七、第三方模块
NodeJs社区或者第三方个人开发、我们可以直接使用的模块。
Node.js的第三方模块通常存在NPM中。Node.js内置了npm。
安装包:npm install module_name --save(-S)--dev(-D)
安装指定版本包:npm install module_name@version
卸载包:npm uninstall module_name
package.json: 记录当前项目所依赖的模块的版本信息
package-lock.json:记录了node_modules目录下所有模块的具体来源、版本号及其它信息
两者保证了在协同开发时,大家所依赖的模块版本是一致的。
热更新工具:npm install nodemon -g
|