1️⃣ Node.js

简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。

1️⃣ Node.js 有什么特点

文件的读写,数据的读写( I:input:写 / O:output:读 )I/O操作

2️⃣ 优点

异步非阻塞的 I/O( I/O线程池 )
特别适用于 I/O 密集型应用 ( 频繁操作 I/O 就属于 I/O 密集型 )
事件循环机制( 独有的一套,与浏览器不一样 )
单线程(成也单线程,败也单线程)
跨平台( 几乎常见的语言都支持 )
简单web交互模型:
image.png

2️⃣ 缺点

回调函数嵌套太多、太深( 俗称回调地狱 )
单线程,处理不好 CPU 密集型任务 ( 请求密集型 )

1️⃣ Node 与 Java 服务器的区别

java服务端一个线程处理一个请求,一对一的模式,客户端发出请求,服务端发送到数据库,这个单线程就单独处理这个请求,数据没有回来之前,再次发送请求时再次在服务端打开一个单线程处理请求。
node服务端一个线程处理多个请求,一对多的模式,客户端发出请求,服务端发送到数据库,在数据回来之前服务端可以处理第二个请求,如果第一个数据获取成功,这返回处理第一个数据。

1️⃣ Node 中函数的特点

Node 中任何一个模块( js 文件 )都被一个看不见的外层函数所包裹

  1. function (exports, require, module, __filename, __dirname) {
  2. console.log(arguments.callee.toString()); // 外层就是包裹的外层函数( 匿名函数 )
  3. }
  4. // 为什么要设计这个外层函数(这个外层函数有什么作用?)
  5. // 1).用于支持模块化语法
  6. // 2).隐藏服务器内部实现(从作用域角度去看)
  7. exports:用于支持 CommonJs 模块化规范的暴露语法
  8. require:用于支持 CommonJs 模块化规范的引入语法
  9. module:用于支持 CommonJs 模块化规范的暴露语法
  10. __filename:当前运行文件的绝对路径
  11. __dirname:当前运行文件所在文件夹的绝对路径

1️⃣ Node 中的 global

node 禁止了函数中的 this 指向 global,输出 global 直接使用 global

2️⃣ 浏览器端 js 的组成部分

BOM - window 浏览器对象模型
DOM - document 文档对象模型
ES规范

2️⃣ Node js 的组成部分

几乎包含了所有 ES 规范
没有了 window 取而代之的是 global

2️⃣ global 的一些常用属性

clearImmediate:清空立即执行函数
clearInterval:清除循环定时器
clearTimeout:清除延迟定时器

setImmediate:设置立即执行函数
setInterval:设置循环定时器
setTimeout:设置延迟定时器

1️⃣ Node 中的事件循环模型

第一个阶段:timers ( 定时器阶段—setTimeout,setInterval )

  1. 1. 开始计时
  2. 2. 执行定时器的回调

第二个阶段:pending callbacks (系统阶段)
第三个阶段:idle, prepare (准备阶段)
第四个阶段:poll( 轮询阶段,核心 )

  1. 1. 如果回调队列里有待执行的回调函数
  2. 1. 从回调队列中取出回调函数,同步执行(一个一个执行),直到回调队列为空了,或者达到系统最大限度。
  3. 2. 如果回调队列为空
  4. 1. 如果有设置过setImmediate
  5. 1. 进入下一个check阶段,目的:为了执行setImmediate所设置的回调。
  6. 2. 如果未设置过setImmediate
  7. 1. 在此阶段停留,等待回调函数被插入回调队列。
  8. 2. 若定时器到点了,进入下一个check阶段,原因:为了走第五阶段,随后走第六阶段,随后第一阶段( 最终目的 )

第五个阶段:check ( 专门用于执行 setImmediate 所设置的回调 )
第六个阶段:close callbacks ( 关闭回调阶段 )
process.nextTick() —— 用于设置立即执行函数( “VIP”——-能在任意阶段优先执行 )

  1. //延迟定时器
  2. setTimeout(()=>{
  3. console.log('setTimeout所指定的回调函数执行了')
  4. })
  5. //立即执行函数(回调)
  6. setImmediate(()=>{
  7. console.log('我是setImmediate指定的回调')
  8. })
  9. //立即执行函数(回调)
  10. process.nextTick(()=>{
  11. console.log('process.nextTick所指定的回调执行了')
  12. })
  13. console.log('我是主线程上的代码')
  14. // Node 执行顺序
  15. 我是主线程上的代码
  16. process.nextTick所指定的回调执行了
  17. setTimeout所指定的回调函数执行了
  18. 我是setImmediate指定的回调
  19. 注意:setTimeout setImmediate 的先后与主线程是否有代码和 setTimeout 设置时间的长短有关系

1️⃣ Node 中的文件读取与写入

2️⃣ Node中的文件系统

在NodeJs中有一个文件系统,所谓的文件系统,就是对计算机中的文件进行增删改查等操作。
在NodeJs中,给我们提供了一个模块,叫做fs模块(文件系统),专门用于操作文件。
fs模块是Node的核心模块,使用的时候,无需下载,直接引入。

2️⃣ 简单文件写入 - 异步文件写入

简单文件写入和简单文件读取,都是一次性把所有要读取或要写入的内容加到内存中红,容易造成内存泄露。
fs.writeFile(file, data,[options], callback)

  1. 1. file:要写入的文件路径+文件名+后缀
  2. 2. data:要写入的数据
  3. 3. options:配置对象 ( 可选参数 )
  4. 1. encoding:设置文件的编码方式,默认值:utf8 ( 万国码 )
  5. 2. mode:设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
  6. 1. 0o111:文件可被执行的权限 .exe .msc 几乎不用,linux 有自己一套操作方法。
  7. 2. 0o222:文件可被写入的权限
  8. 3. 0o444:文件可被读取的权限
  9. 3. flag:打开文件要执行的操作,默认值是 'w'
  10. 1. a :追加
  11. 2. w :写入
  12. 4. callback:回调函数
  13. 1. err:错误对象
  14. 5. Node中有这样一个原则:错误优先
  1. // 写入文件的基本操作
  2. // 引入内置的fs模块
  3. let fs = require('fs')
  4. // 调用 writeFile 方法
  5. // Node.js 中,__dirname 总是指向被执行 js 文件的绝对路径
  6. fs.writeFile(__dirname + '/demo.txt', '写入的文字', {
  7. mode: 0o666, // 可不写 - 默认值
  8. flag: 'w' // 可不写 - 默认值
  9. }, (err) => {
  10. if (err) {
  11. console.log(`写入失败:${err}`);
  12. } else {
  13. console.log(`写入成功`);
  14. }
  15. })
  16. fs.writeFile(__dirname + '/demo.txt', '写入的文字 - 追加', {
  17. mode: 0o666,
  18. flag: 'a'
  19. }, (error) => {
  20. if (error) {
  21. console.log(`写入失败:${error}`);
  22. } else {
  23. console.log(`写入成功`);
  24. }
  25. })

2️⃣ 简单文件读取

fs.readFile(path[, options], callback)

  1. 1. path:要读取文件的路径+文件名+后缀
  2. 2. options:配置对象(可选)
  3. 3. callback:回调
  4. 1. err:错误对象
  5. 2. data:读取出来的数据
  1. let fs = require('fs')
  2. fs.readFile(__dirname + '/demo.txt', function (err, data) {
  3. if (err) {
  4. console.log(err)
  5. } else {
  6. console.log(data) // 读取出来的数据为 Buffer 格式
  7. }
  8. // 将读取的文件写入另一个文件
  9. fs.writeFile('../demo1.txt', data, {
  10. flag: 'a'
  11. }, (err) => {
  12. if (err) {
  13. console.log(`写入失败:${err}`);
  14. } else {
  15. console.log(`写入成功`);
  16. }
  17. })
  18. })

2️⃣ 流式文件写入

fs.createWriteStream(path,[options])

  1. 1. path:要写入文件的路径+文件名+文件后缀
  2. 2. options:配置对象(可选参数)
  3. 1. flags:打开文件要执行的操作,默认值是 'w'
  4. 2. encoding :设置文件的编码方式,默认值:utf8 ( 万国码 )
  5. 3. fd : 文件统一标识符,linux下文件标识符
  6. 4. mode :设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
  7. 1. 0o111:文件可被执行的权限 .exe .msc 几乎不用,linux 有自己一套操作方法。
  8. 2. 0o222:文件可被写入的权限
  9. 3. 0o444:文件可被读取的权限
  10. 5. autoClose:自动关闭 - 关闭流时执行自动关闭文件,默认值:true
  11. 6. emitClose:强制关闭 - 检测到某些问题时强制关闭文件,默认值:false
  12. 7. start:写入文件的起始位置( 写入文件前边的空格缩进 - 偏移量 )
  1. // 引入内置的fs模块
  2. let fs = require('fs')
  3. //创建一个可写流
  4. let ws = fs.createWriteStream(__dirname + '/demo.txt', {
  5. start: 10
  6. })
  7. // 监测流的状态 - 只要用到了流,就必须检测流的状态
  8. ws.on('open', function () {
  9. console.log('可写流打开了')
  10. })
  11. ws.on('close', function () {
  12. console.log('可写流关闭了')
  13. })
  14. //使用可写流写入数据
  15. ws.write('第一次 \n')
  16. ws.write('第二次 \n')
  17. ws.write('第三次 \n')
  18. // ws.close() 如果在 Node 的 8 版本中,使用此方法关闭流会造成数据丢失
  19. ws.end() // 在 Node 的 8 版本中,要用 end 方法关闭流

2️⃣ 流式文件读取

fs.createReadStream(path[, options])

  1. 1. path:要读取的文件路径+文件名+后缀
  2. 2. options
  3. 1. flags:打开文件要执行的操作,默认值是 'w'
  4. 2. encoding :设置文件的编码方式,默认值:utf8 ( 万国码 )
  5. 3. fd : 文件统一标识符,linux下文件标识符
  6. 4. mode :设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
  7. 5. autoClose:自动关闭 - 关闭流时执行自动关闭文件,默认值:true
  8. 6. emitClose:强制关闭 - 检测到某些问题时强制关闭文件,默认值:false
  9. 7. start:起始偏移量
  10. 8. end:结束偏移量
  11. 9. highWaterMark:每次读取数据的大小,默认值是 64 * 1024
  1. let {
  2. createReadStream,
  3. createWriteStream
  4. } = require('fs');
  5. //创建一个可读流
  6. let rs = createReadStream(__dirname + '/demo.mp3', {
  7. highWaterMark: 10 * 1024 * 1024,
  8. //start:60000,
  9. //end:120000
  10. })
  11. // 创建一个可写流
  12. let ws = createWriteStream('../demo1.mp3')
  13. // 只要用到了流,就必须监测流的状态
  14. rs.on('open', function () {
  15. console.log('可读流打开了')
  16. })
  17. rs.on('close', function () {
  18. console.log('可读流关闭了')
  19. ws.close() // 在数据读取完毕之后关闭可写流
  20. })
  21. ws.on('open', function () {
  22. console.log('可写流打开了')
  23. })
  24. ws.on('close', function () {
  25. console.log('可写流关闭了')
  26. })
  27. // 给可读流绑定一个 data 事件,就会触发可读流自动读取内容。
  28. rs.on('data', function (data) {
  29. // Buffer 实例的 length 属性,是表示该 Buffer 实例占用内存空间的大小
  30. console.log(data.length) // 输出的是 65536,每次读取 64KB 的内容
  31. ws.write(data)
  32. // ws.close() // 若在此处关闭流,会写入一次,后续数据丢失
  33. })
  34. // ws.close() // 若在此处关闭流,导致无法写入数据

1️⃣ Node 原生服务器

  1. // 原生 node 搭建最简单的服务器
  2. // 引入 node 内置的 http 模块
  3. let http = require('http');
  4. // 创建服务对象
  5. let server = http.createServer(function (request, response) {
  6. response.end('ok')
  7. })
  8. // 指定服务器的运行端口号( 绑定端口监听 )
  9. server.listen(3000, function (err) {
  10. if (!err) console.log('服务器启动成功了')
  11. else console.log(err)
  12. })
  13. // 原生 node 搭建最简单的服务器( 详细版 )
  14. // 引入 node 内置的 http 模块
  15. let http = require('http');
  16. //引入一个内置模块,用于解析 key=value&key=value.....这种形式的字符串为 js 中的对象
  17. //备注:
  18. // 1.key=value&key=value.....的编码形式:urlencoded 编码形式。
  19. // 2.请求地址里携带urlencoded编码形式的参数,叫做:查询字符串参数(query参数)。
  20. //引入的 qs 是一个对象,该对象身上有着很多有用的方法,最具代表性的:parse(),会解析 urlencoded 编码
  21. let qs = require('querystring')
  22. // 创建服务对象
  23. let server = http.createServer(function (request, response) {
  24. // (1).request:请求对象,里面包含着客户端给服务器的 “ 东西 ”
  25. // (2).response:响应对象,里面包含着服务器要返回给客户端的 “ 东西 ”
  26. // 获取客户端请求方式
  27. if (request.method.toUpperCase() === 'POST') {
  28. console.log('使用POST请求');
  29. } else if (request.method.toUpperCase() === 'GET') {
  30. console.log('使用GET请求')
  31. //获取客户端 get 请求携带过来的 urlencoded 编码形式的参数
  32. let params = request.url.split('?')[1];
  33. // 将参数转换为对象
  34. let objParams = qs.parse(params);
  35. // 如果知道参数都有什么 使用结构赋值取出每一个参数
  36. let {
  37. name,
  38. age
  39. } = objParams;
  40. // 设置响应头的编码格式 让浏览器认识相应的数据
  41. response.setHeader('content-type', 'text/html;charset=utf-8');
  42. // 相应数据
  43. response.end(`<h1>姓名:${name} --- 年龄:${age}`);
  44. }s
  45. })
  46. // 指定服务器的运行端口号( 绑定端口监听 )
  47. server.listen(3000, function (err) {
  48. if (!err) {
  49. console.log('服务器启动成功了')
  50. } else {
  51. console.log(err)
  52. }
  53. })