这是我参与「第四届青训营 」笔记创作活动的的第十五天。 今天学习了《NodeJS与前端开发实战》,跟着老师的讲解,从NodeJs的应用到结构,再到手动搭建一个简单的HttpServer,让我对NodeJs的理解更加深入了一步,Node.js是一个专注于实现高性能Web服务器优化的专家,几经探索,几经挫折后,遇到V8而诞生的项目。Node.js是一个让JavaScript运行在服务器端的开发平台 ,所以,不管现在还是以后工作,NodeJs都是一大利器。
本堂课重点内容
- NodeJS的应用场景
- NodeJS的运行结构
- 搭建HttpServer
详细知识点介绍
详细知识点总结在文章的最后,坚持打卡,学习!
课后个人总结
对于NodeJS,我的理解就是可以运行在后端的JavaScript。 为什么它能够在后端运行呢? 这就得归功于V8引擎(V8是Google Chrome浏览器的JavaScript引擎),通过对高性能V8引擎的封装,并通过一系列优化的API类库,使其就能够在后端运行了。
NodeJS可以让我们脱离后端自己编写所需要的接口,但是这并不代表我们以后都不需要后端了。Node对于i/o流的操作,在目前的主流后端语言中数一数二。基于这种特性,很多公司都是用NodeJS做项目搭建工具,这就说明我们学习NodeJS是必要的。
笔记
NodeJS与前端开发实战
1、NodeJs的应用场景
- 前端工程化
- Web服务端应用
- Electron跨桌面应用
(1)前端工程化
Bundle:webpack,vite,esbuild,parcelUglify:uglifyjsTranspile:bablejs,typescript- 其他语言加入竞争:
esbuild,parcel,prisma - 现状:难以替代
(2)Web服务端应用
- 学习曲线平缓,开发效率较高
- 运行效率接近常见的编译语言
- 社区生态丰富及工具链成熟(npm, V8 inspector)
- 与前端结合的场景会有优势(SSR)
- 现状:竞争激烈,
Node.js有自己独特的优势
(3)Electron跨桌面应用
- 商业应用:
vscode,slack,discord,zoom - 大型公司内的效率工具
- 现状:大部分场景在选型时,都值得考虑
2、NodeJS运行时结构
[NodeJS运行时结构] github.com/joyeecheung…
(1)运行时结构
V8:JavaScript Runtime,诊断调试工具(inspector)libuv:eventloop(事件循环),syscall(系统调用)
(2)特点
- 异步 I / O
- 单线程
- 跨平台
① 异步 I / O
当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待。
② 单线程
JS单线程
实际:JS线程+uv线程池+V8任务线程池+V8 Inspector线程
-
优点:不用考虑多线程状态同步问题,也就不需要锁;同时还能比较高效地利用系统资源
-
缺点:阻塞会产生更多负面影响
- 解决办法:多进程或多线程
③ 跨平台
-
跨平台(大部分功能、api)
-
Node.js 跨平台+JS无需编译环境(+ Web跨平台+诊断工具跨平台)
- ·=开发成本低(大部分场景无需担心跨平台问题),整体学习成本低
3、编写Http Server
- 安装NodeJS
- 编写Http Server +Client,收发GET,POST 请求
- 编写静态文件服务器
- 编写React SSR 服务
- 适用inspector进行调试、诊断
- 部署简介
(1)安装NodeJS
- Mac,Linux推荐使用nvm。多版本管理。
- Windows推荐nvm4w 或是官方安装包。
- 安装慢,安装失败的情况,设置安装源。
(2)编写HttpServer
① http_server.js
const http = require('http')
const port = 3000
const server = http.createServer((req, res) => {
res.end('hello')
})
server.listen(port, () => {
console.log('listening on', port)
})
② http_server_json.js
const http = require('http')
const server = http.createServer((req, res) => {
const bufs = [];
req.on('data', data => {
bufs.push(data);
})
req.on('end', () => {
const buf = Buffer.concat(bufs).toString('utf-8')
let msg ='hello'
try {
const ret = JSON.parse(buf)
msg = ret.msg
} catch (err) {
}
const responseJson = {
msg: `receive: ${msg}`
}
res.setHeader('Content-Type','application/json')
res.end(JSON.stringify(responseJson))
})
})
const port = 3000
server.listen(port, () => {
console.log('listening on', port)
})
③ http_client.js
const http = require('http')
const server = http.createServer((req, res) => {
const bufs = [];
req.on('data', data => {
bufs.push(data);
})
req.on('end', () => {
const buf = Buffer.concat(bufs).toString('utf-8')
let msg ='hello'
try {
const ret = JSON.parse(buf)
msg = ret.msg
} catch (err) {
}
const responseJson = {
msg: `receive: ${msg}`
}
res.setHeader('Content-Type','application/json')
res.end(JSON.stringify(responseJson))
})
})
const port = 3000
server.listen(port, () => {
console.log('listening on', port)
})
④ Promisify
用Promise + async await 重写这两个例子(why)
技巧:将callback 转换成promise
http_server_json.js
const http = require('http')
const server = http.createServer(async (req, res) => {
// receive body from client
const msg = await new Promise((resolve, reject) => {
const bufs = [];
req.on('data', data => {
bufs.push(data)
})
req.on('error', (err) => {
reject(err)
})
req.on('end', () => {
const buf = Buffer.concat(bufs).toString('utf-8')
let msg = 'hello'
try {
const ret = JSON.parse(buf)
msg = ret.msg
} catch (err) {
}
resolve(msg)
})
})
// response
const responseJson = {
msg: `receive: ${msg}`
}
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(responseJson))
})
const port = 3000
server.listen(port, () => {
console.log('listening on', port)
})
(3)静态文件
编写一个简单的静态文件服务器:
const http = require('http')
const fs = require('fs')
const path = require('path')
const url = require('url')
const folderPath = path.resolve(__dirname, './static')
const server = http.createServer((req, res) => {
// expected http://127.0.0.1:3000/index.html
const info = url.parse(req.url)
// static/index.html
const filePath = path.resolve(folderPath, './' + info.path)
//stream api
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
})
const port = 3000
server.listen(port, () => {
console.log('listening on', port)
})
与高性能、可靠的服务相比,还差什么?
1、CDN:缓存 + 加速
2、分布式存储,容灾
(4)React SSR
SSR (server side rendering)有什么特点?
-
相比传统HTML模版引擎:避免重复编写代码
-
相比SPA (single page application):首屏渲染更快,SEO友好
-
缺点:
- 通常qps较低,前端代码编写时需要考虑服务端渲染情况
const http = require('http')
const port = 3000
const server = http.createServer((req, res) => {
res.setHeader(' Content-Type ', 'text/html')
res.end(`
<DOCTYPE html><html>
<head>
<title>My App</title>
</head>
</html>
<body>
<h1>My App</h1>
</body>
</html>`)
})
server.listen(port, () => {
console.log('listening on', port)
})
SSR难点:
- 1、需要处理打包代码
- 2、需要思考前端代码在服务端运行时的逻辑
- 3、移除对服务端无意义的副作用,或重置环境
(5)Debug调试
V8 Inspector:开箱即用、特性丰富强大、与前端开发一致、跨平台。
node --inspectopen http:/ /localhost:9229/json
场景:
- 查看
console.log内容 breakpoint- 高CPU、死循环:
cpuprofile - 高内存占用:
heapsnapshot - 性能分析
(6)部署
-
部署要解决的问题
- 守护进程:当进程退出时,重新拉起
- 多进程:cluster便捷地利用多进程
- 记录进程状态,用于诊断
-
容器环境
- 通常有健康检查的手段,只需考虑多核cpu利用率即可