这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
一、本堂课重点内容:
- Node.js应用场景
- Node.js运行结构
- 编写Http Server实战
- 拓展
二、详细知识点介绍:
Node.js应用场景
前端工程化
-
打包工具:如webpack
-
代码压缩:做一些转换,减少代码体积。如uglifyjs
-
语法转化:如TypeScript
-
现状:难以替代,学习成本低
Web服务端应用
-
学习曲线平缓,开发效率较高
-
运行效率接近常见编译语言,处于脚本语言前列
-
社区生态丰富及工具链成熟
-
与前端结合的场景有优势
小结:竞争激烈,但Node.js有自己独特优势,有很强的竞争力
Electron跨端桌面应用
-
商业应用:vscode
-
大型公司内的效率工具
Electron:开发效率高,跨端,运行稳定
字节应用
BFF应用:需要很多服务端接口的场景,端可以自己构建所需要的接口拼接API,对web端开发很有帮助
SSR应用
服务端应用:头条搜索、西瓜视频
Electron:飞书
每年新增1000+ Node.js应用
运行结构
底层:
V8:JavaScript Runtime,诊断调试工具
libuv:eventloop事件循环,syscall系统调用
举例:用node-fecth发起请求时
通过npm安装该模块后,在用户代码中调用该模块时,然后会到V8中执行,还会调用Node.js Core(JavaScript)中的HTTP模块,接着会进一步调用下一层Node.js Core(C++)的API,接着调用底层的llhttp帮助进行http协议的序列化及反序列化,所得数据通过libuv创建TCP连接,发给远端,然后从远端收到的数据再逆向走一遍上述流程,最终在用户代码中得到整个数据
特点
- 异步I/O:
Node.js执行I/O操作时,会在响应返回后恢复操作,并不会阻塞线程并占用额外内存,是一个异步调用,有更多内存可用,从而提高性能、效率
- 单线程:
worker_thread 可以起独立线程
实际:JS线程+libuv线程池+V8任务线程池+V8 inspector线程(调试工具等有单独线程,不然怎么调试死循环?)
严格来讲,是主线程为单线程
优点:不用考虑多线程状态同步问题,无需锁,能高效利用系统资源
缺点:阻塞会产生更多负面影响(有更多任务会被干涉),可用多进程解决
- 跨平台(大部分功能API)
Node.js跨平台+JS无需编译环境+Web跨平台+诊断工具跨平台=开发成本低
Http Server实战
主要是代码实战,具体在下一部分
-
编写Http Server + Client ,收发 GET POST
-
编写静态文件服务器
还需要CDN提供缓存+加速
分布式储存:容灾
- 编写React SSR服务
SSR难点
需要处理打包代码
需要思考前端代码在服务端运行的逻辑
移除对服务端无意义的副作用,或重置环境
使用Inspector进行调试、诊断
V8 Inspector:功能强大,跨平台
- 部署简介
守护进程:进程退出时重新拉起
多进程:cluster便捷利用多进程
记录进程状态用于诊断
容器环境检查
拓展
尝试去为Node.js贡献代码
好处:
-
从使用者的角色逐步理解底层细节,可以解决更复杂的问题
-
自我证明,有助于职业发展
-
解决社区问题,促进社区发展
学习编译Node.js
学习诊断/追踪
技术咨询行业
还有WASM,NAPI等等
三、实践练习例子:
简易Http server
const http = require('http')
const port = 3000
const server = http.createServer((req,res) => {
res.end('hello')
})
server.listen(port, () => {
console.log('listening on: ',port)
})
JSON
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', () => {
const buf = Buffer.concat(bufs).toString('utf8')
let msg = 'Hello Youth camp'
try{
const ret = JSON.parse(buf)
msg = ret.msg
} catch (err){
// res.end('invalid json')
}
const responseJson = {
msg: `receive: ${msg}`
}
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(responseJson))
})
})
server.listen(port, () => {
console.log('listening on: ',port)
})
用Promisify重写JSON
const http = require('http')
const port = 3000
const server = http.createServer(async (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', () => {
const buf = Buffer.concat(bufs).toString('utf8')
let msg = 'Hello Youth camp'
try {
const ret = JSON.parse(buf)
msg = ret.msg
} catch (err) {
// res.end('invalid json')
}
resolve(msg)
})
})
const responseJson = {
msg: `receive: ${msg}`
}
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(responseJson))
})
server.listen(port, () => {
console.log('listening on: ',port)
})
简易Http Client
const http = require('http')
const body = JSON.stringify({
msg: 'Hello from cgy s client',
})
const req = http.request('http://127.0.0.1:3000', {
method: 'POST',
headers:{
'Content-Type':'application/json'
}
}, (res) => {
const bufs = []
res.on('data',(buf) =>{
bufs.push(buf)
})
res.on('end', () => {
const buf = Buffer.concat(bufs)
const json = JSON.parse(buf)
console.log('json.msg is: ', json.msg)
})
})
req.end(body)
简易静态文件服务
const http = require('http')
const fs = require('fs')
const path = require('path')
const url = require('url')
const folderPath = path.resolve(__dirname,'./static')
const port = 3000
const server = http.createServer((req,res) => {
const info = url.parse(req.url)
const filepath = path.resolve(folderPath,'./' + info.path)
console.log('filepath', filepath)
const filestream = fs.createReadStream(filepath)
filestream.pipe(res)
})
server.listen(port, () => {
console.log('listening on: ',port)
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>static-test</title>
</head>
<body>
<h1>Hello youth camp</h1>
<script>
alert('yes')
</script>
</body>
</html>
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 lang="en">
<head>
<meta charset="UTF-8">
<title>static-test</title>
</head>
<body>
${ReactDOMServer.renderToString(
React.createElement(App,{}, 'my_content'))}
<script>
alert('yes')
</script>
</body>
</html>
`)
})
const port = 3000
server.listen(port,() => {
console.log('listening on: ',port)
})
需要:
npm i react react-dom
小结
由于是跟着老师的摸索式实践,所以理解不深,课下还需要慢慢钻研读代码。
四、课后个人总结:
在今天的课程中,主要讲解了node.js的一些基础知识以及一些简单的代码实践。虽然内容不多,但都很值得学习和消化。其中,老师对node.js的运行结构讲解比较细致,这种对于底层逻辑的讲解虽然对于小白的我而言较为晦涩难懂,但如果搞懂了,那么将收获很多。理解了底层逻辑,那么对于代码编写会有很大的提升。然后后半段的代码实践是跟着老师一步步去敲代码,出现了不一样的错误就逐步排查,这个过程我也学到了许多。
今日笔记做得较为匆忙,如有错漏请指出,谢谢。