node
nodejs就是基于js语法增加与操作系统之间的交互
node版本管理
1 2 3
| curl -o- https: sh/nvm/v0.35.3/install.sh | bash
|
nodejs工作原理
- application 编写的程序
- V8 引擎 解析二进制代码
- 调用nodejs
- 调用libuv事件处理库
循环等待事件处理,有事件进来后先插入至事件队列
nodejs事件处理
- 收到请求
- 生成事件插入到事件队列
- libvu通过事件循环,从事件队列中取出事件处理
- 最终结果-直接执行callback
- 从线程池取出线程做响应处理后回收线程
- 中间结果-重新插入事件队列
两个v8引擎
- 请求之前,nodejs通过v8引擎运行服务
- 客户端发送请求
- 客户端收到服务端返回的js
- 客户端的v8进行解析
最简单的http服务
- require 引入 http模块
- 创建http服务
- 侦听端口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 'use strict'
const http = require('http');
const host = 'localhost' const port = 3333 const app = http.createServer((req, res) => { console.log(req) res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('hello world') }).listen(port, host, () => { console.log(`http://${host}:${port}`) });
|
启动方式
- node app.js
- nohub node app.js &
- forever start app.js
forever stop app.js
创建https服务
1 2 3
| mkcert -install
mkcert localhost 127.0.0.1 ::1
|
- 引入Https模块
- 指定证书位置,并创建Https服务
你访问的不是私密链接解决办法
控制台输入 sendCommand(SecurityInterstitialCommandId.CMD_PROCEED)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 'use strict'
const https = require('https'); const fs = require('fs');
const host = 'localhost' const port = 8080 const options = { key: fs.readFileSync('./ca/local.shanyue.tech-key.pem'), cert: fs.readFileSync('./ca/local.shanyue.tech.pem'), }
const app = https.createServer(options, (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('hello world') }).listen(port, host, () => { console.log(`https://${host}:${port}`) })
|
真正的Web服务
- 引入 express 模块
- 引入serve-index模块
- 指定发布目录
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
| 'use strict'
const http = require('http'); const https = require('https');
const fs = require('fs');
const express = require('express'); const serveIndex = require('serve-index');
const app = express() app.use(serveIndex('./public')) app.use(express.static('./public'))
const options = { key: fs.readFileSync('./ca/local.shanyue.tech-key.pem'), cert: fs.readFileSync('./ca/local.shanyue.tech.pem'), } const httpServe = http.createServer(app); const httpsServe = https.createServer(options, app);
httpServe.listen(80, '0.0.0.0', () => { console.log('http://0.0.0.0') }) httpsServe.listen(8080, '0.0.0.0', () => { console.log('https://0.0.0.0:8080') })
|
node.js的底层依赖
- v8引擎:主要是js语法的解析,识别js语法
- libuv:C语言实现的一个高性能异步非阻塞IO库,实现node.js的事件循环
- http-parse/||http: 底层处理http请求,处理报文,解析请求包等内容
- openssl:处理加密算法
- zlib: 处理压缩等内容
node.js常见内置模块
node.js 中最主要的内容,就是实现了一套 CommonJS 的模块化规范,以及内置了一些常⻅的模块。
- fs: 文件系统,能够读取写入当前安装系统环境中硬 盘的数据
- path: 路径系统,能够处理路径之间的问题
- crypto: 加密相关模块,能够以标准的加密方式对我 们的内容进行加解密
- dns: 处理 dns 相关内容,例如我们可以设置 dns 服 务器等等
- http: 设置一个 http 服务器,发送 http 请求,监听 响应等等
- readline: 读取 stdin 的一行内容,可以读取、增加、 删除我们命令行中的内容
- os: 操作系统层面的一些 api,例如告诉你当前系统类 型及一些参数
- vm: 一个专⻔处理沙箱的虚拟机模块,底层主要来调 用 v8 相关 api 进行代码解析。
实现简易commonjs
- 读取文件并解析执行 vm.Script 作用是把字符串变为可执行代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const vm = require('vm') const path = require('path') const fs = require('fs')
const pathToFile = path.resolve(__dirname, './index1.js'); const content = fs.readFileSync(pathToFile, 'utf-8')
const script = new vm.Script(content, { filename: 'index1.js' })
const result = script.runInThisContext()
|
- 注入require函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const vm = require('vm') const path = require('path') const fs = require('fs')
const pathToFile = path.resolve(__dirname, './index2.js'); const content = fs.readFileSync(pathToFile, 'utf-8')
const wrapper = [ '(function(require, module, exports){', '})' ]
const wrapperContent = wrapper[0] + content + wrapper[1]
const script = new vm.Script(wrapperContent, { filename: 'index1.js' })
const result = script.runInThisContext() console.log(result)
|
- 注入 module exports 并挂载
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
| const vm = require('vm') const path = require('path') const fs = require('fs')
function r(filename) { const pathToFile = path.resolve(__dirname, filename); const content = fs.readFileSync(pathToFile, 'utf-8')
const wrapper = [ '(function(require, module, exports){', '})' ]
const wrapperContent = wrapper[0] + content + wrapper[1]
const script = new vm.Script(wrapperContent, { filename: 'index1.js' })
const result = script.runInThisContext() const module = { exports: {
} } result(r, module, module.exports) return module.exports }
global.r = r
|
全局对象
process 是一个全局变量,即 global 对象的属性。它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。
比较常用的:
process.env 环境变量
process.argv 获取命令行输入参数 实现cli的commander底层就是使用的这个
- exit
当进程准备退出时触发。
- beforeExit
当 node 清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 ‘beforeExit’ 的监听器可以异步调用,这样 node 就会继续执行。
- uncaughtException
当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。
- Signal 事件
当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。
1 2 3 4 5 6 7 8 9 10
| process.on('exit', function(code) { setTimeout(function() { console.log("setTimeout"); }, 0);
console.log('退出码为:', code);
});
console.log("程序执行结束");
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| / 输出到终端
process.stdout.write("Hello World!" + "\n");
// 通过参数读取
process.argv.forEach(function(val, index, array) { console.log(index + ': ' + val); });
// 获取执行路局 console.log(process.execPath);
// 平台信息 console.log(process.platform);
|
看看这段代码输出什么
1 2 3
| console.log(this); module.exports.foo = 5; console.log(this);
|
事件循环
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
| async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') setTimeout(function () { console.log('setTimeout0') setTimeout(function () { console.log('setTimeout1'); }, 0); setImmediate(() => console.log('setImmediate')); }, 0)
process.nextTick(() => console.log('nextTick')); async1(); new Promise(function (resolve) { console.log('promise1') resolve(); console.log('promise2') }).then(function () { console.log('promise3') }) console.log('script end')
|
在 Node 领域,微任务是来自以下对象的回调:
process.nextTick()
then()
优先级 process.nextTick > promise.then
setImmediate 和 setTimeout 的区别
setImmediate 和 setTimeout 相似,但是根据调用时间的不同,它们的行为也不同。
Tips: 计时器的执行顺序将根据调用它们的上下文而有所不同。 如果两者都是主模块中调用的,则时序将受到进程性能的限制.
来看两个例子:
在主模块中执行
1 2 3 4 5 6 7 8
| setTimeout(() => { console.log('timeout'); }, 0);
setImmediate(() => { console.log('immediate'); });
|
两者的执行顺序是不固定的, 可能timeout在前, 也可能immediate在前
在同一个I/O回调里执行
1 2 3 4 5 6 7 8 9 10
| const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0);
setImmediate(() => { console.log('immediate'); }); });
|
在同一个I/O回调里执行 setImmediate总是先执行
nodejs周边生态