[ Node.js与前端开发实战 | 青训营笔记]

55 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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应用

运行结构

image-20230131145954834.png

底层:

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)
})

image-20230131164248302.png

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)
})

image-20230131172437371.png

用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)
})

image.png

简易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)

image-20230131175901219.png

简易静态文件服务

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>

image-20230131212517946.png

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)
})

image-20230131215315563.png 需要:

npm i react react-dom

小结

由于是跟着老师的摸索式实践,所以理解不深,课下还需要慢慢钻研读代码。

四、课后个人总结:

在今天的课程中,主要讲解了node.js的一些基础知识以及一些简单的代码实践。虽然内容不多,但都很值得学习和消化。其中,老师对node.js的运行结构讲解比较细致,这种对于底层逻辑的讲解虽然对于小白的我而言较为晦涩难懂,但如果搞懂了,那么将收获很多。理解了底层逻辑,那么对于代码编写会有很大的提升。然后后半段的代码实践是跟着老师一步步去敲代码,出现了不一样的错误就逐步排查,这个过程我也学到了许多。

今日笔记做得较为匆忙,如有错漏请指出,谢谢。