这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
Node.js 应用场景
-
前端工程化(……)
- Bundle webpack、vite、esbuild、parcel
- Uglify uglifyjs
- Transpile babeljs、typescript
- esbuild、parcel、prisma
-
Web 服务端应用(Node.js、Vercel)
- 学习曲线平缓、开发效率高
- 运行效率接近常见的编译语言
- 社区生态丰富、工具链成熟(npm、V8 inspector)
- 与前端结合场景有优势(SSR)
-
Electron 跨端桌面应用
- 商业应用(vscode、slack、discord、zoom)
- 大型公司内的效率工具
字节内部
BFF、SSR Modern.js
服务端应用
Electron应用 飞连、飞书
Node.js 运行时结构
Node 结构
- Node.js Core 由 JavaScript 或 C++ 语言编写
- N-API 使用其他编程语言(除 JS 和 C++之外)写的模块
- V8 JavaScript Runtime 及 诊断调试工具 inspector
- libuv 封装操作系统 API,如 eventloop 事件循环、 syscall 系统调用等
- nghttp2 http相关
- zlib 压缩文件相关
- c-ares DNS 查询相关
- llhttp http协议解析
- openSSL ssl加密协议解析
运行时特点
- 异步 I/O
- I/O 操作异步执行
- 单线程
- 实质 JS 线程 + uv 线程池 + V8 任务线程池 + V8 Inspector 线程
- 不用考虑多线程状态同步问题,能够较高效地利用系统资源
- 阻塞会产生更多负面影响,可使用多进程或者多线程解决
- 跨平台
-
大多数 API 均跨平台
-
开发成本较低
编写 HTTP Server
编写 HTTP Server
const http = require('http')
const port = 3000
const server = http.createServer((req, res) => {
const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})
req.on('end', () => {
try {
const data = JSON.parse(Buffer.concat(bufs).toString('utf8'))
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
echo: data.msg || 'Hello',
}))
} catch (err) {
res.end('<h1>invalid json</h1>')
}
})
})
server.listen(port, () => {
console.log(`server listens on: ${port}`)
})
转换为 promise
const http = require('http')
const server = http.createServer((req, res) => {
const msg = await new Promise((resolve, reject) => {
const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})
req.on('error', (err) => {
reject(err)
})
req.on('end', () => {
try {
const data = JSON.parse(Buffer.concat(bufs).toString('utf8'))
resolve(data.msg)
} catch (err) {
reject(err)
}
})
})
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
echo: msg || 'Hello',
}))
})
const port = 3000
server.listen(port, () => {
console.log(`server listens on: ${port}`)
})
编写静态文件服务器
const http = require('http')
const fs = require('fs')
const path = require('path')
const url = require('url')
const folderPath = path.resolve(__dirname, './')
const server = http.createServer((req, res) => {
const info = url.parse(req.url)
const filePath = path.resolve(folderPath, './', info.path)
const fileStream = fs.createReadStream(filePath)
res.pipe(fileStream)
})
const port = 3000
server.listen(port, () => {
console.log(`server listens on: ${port}`)
})
进阶
CDN - 缓存 + 加速
分布式储存、容灾
编写React SSR服务
SSR - server side rendering 服务端渲染
较 SPA,首屏渲染更快,SEO友好
较传统 HTML 模板引擎,避免重复编写代码
通常 qps 较低,编写前端代码时需考虑服务端渲染情况
const React = require('react')
const ReactDOMServer = require('react-dom/server')
const http = require('http')
function App(props) {
return React.createElement('div', {}, props.children || 'Hello')
}
const server = http.createServer((req, res) => {
res.end(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>M</title>
</head>
<body>
${ReactDOMServer.renderToString(React.createElement(APP, {}, 'content'))}
</body>
</html>
`)
})
const port = 3000;
server.listen(port, () => {
console.log(`server listens on: ${port}`)
})
使用Inspector进行调试和诊断
开箱即用
使用--inspect参数启用 Inspector,之后可在/json路径下查看
查看 console 内容
breakpoint
cpuprofile 高CPU、死循环
heapsnapshot 高内存占用
性能分析
其他
逐步理解 Node.js 的底层细节
支持wasm和NAPI(不同编程语言间通信)