Node.js | 青训营笔记

52 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第9天

一、重点内容:

  • Node.js的应用场景,运行结构
  • 实战:编写Http Server

二、详细知识点介绍:

Node.js 的应用场景

  • 前端工程化

    • 打包Bundle:webpack,vite,esbuild,parcel
    • 压缩Uglify:uglifyjs
    • 转义Transpile:bablejs,typescript
    • 竞争:esbuild,parcel,prisma
    • 现状:难以代替node.js
  • Web服务端应用:node可以做出与传统后端一样的服务

    • 学习曲线平缓,开发效率高
    • 运行效率接近常见的编译语言
    • 社区生态丰富,工具链成熟(npm,V8 inspector)
    • 与前端结合的场景有优势(SSR)
    • 现状:竞争激烈,Node.js有自己独特的优势
  • Electron跨端桌面应用

    • 商业应用:vscode,slack,discord,zoom
    • 大型公司内的效率工具
    • 现状:大部分场景在选型时,都值得考虑

Node.js 运行时结构

V8:JavaScript Runtime,诊断调试工具(inspector)

libuv:eventloop(事件循环),syscall(系统调用)

特点

  • 异步I/O:当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占有额外内存等待
  • 单线程

    • 实际:JS线程+uv线程池(4个)+V8任务线程池+V8 Inspector线程
    • 优点:不用考虑多线程状态同步问题,也就不需要锁;同时还能比较高效地利用系统资源
    • 缺点:阻塞会产生更多负面影响;解决办法:多进程或多线程
  • 跨平台

    • (大部分功能、api)
    • Node.js 跨平台+JS 无需编译环境(+Web跨平台+诊断工具跨平台)=开发成本低,整体学习成本低

编写Http Server

Http Server

const http = require('http')

const port = 3000

const server = http.createServer((req, res) => {
    res.end('hello')
})

server.listen(port, () => {
    console.log(`server listen on :${port}`);
})

用Promise+async await 重写

  • 将callback转换成promise
function wait(t){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve()
        },t)
    })
}

wait(1000).then(()=>{console.log('get')})
const { rejects } = require('assert')
const http = require('http')
const { resolve } = require('path')

const port = 3000

const server = http.createServer(async (req, res) => {
    //接受client里的body数据
    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('utf8')
            let msg = 'hello'
            try {
                const ret = JSON.parse(buf)
                msg = ret.msg
            } catch (err) {
                // res.end('invalid json')
            }
            resolve(msg)
        })
    })

    //响应
    const resJson = {
        msg: `revice: ${msg}`
    }
    res.setHeader('Content-Type', 'application/json')
    res.end(JSON.stringify(resJson))

})

server.listen(port, () => {
    console.log(`server listen on :${port}`);
})

Http Client

const http = require('http')

const body = JSON.stringify({
    msg: 'Hello from 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 port = 3000

const folderPath = path.resolve(__dirname, './static')

const server = http.createServer((req, res) => {
    //expected http://127.0.0.1:3000/index.html?abc=100
    const info = url.parse(req.url)
    console.log('info', info);
    const filepath = path.resolve(folderPath, './' + info.path)
    console.log('filepath', filepath);
    const filestream = fs.createReadStream(filepath)

    filestream.pipe(res)

})

server.listen(port, () => {
    console.log(`server listen on :${port}`);
})

优化

  • CDN:缓存+加速
  • 分布式储存,容灾

React SSR

Server side rendering

  • 相比于传统HTML模板引擎:避免重复编写代码
  • 相比SPA():首屏渲染更快,SEO友好
  • 通常qps较低,前端代码编写时需要考虑服务端渲染情况

HTML例子

难点

  • 需要处理打包代码

  • 需要思考前端代码在服务端运行时的逻辑

  • 移除对服务端无意义的副作用,或重置环境

Debug

V8 Inspector:开箱即用、特性丰富强大、与前端开发一致、跨平台

  • node --inspect

场景

  • 查看console.log内容
  • Breakpoint
  • 高CPU、死循环:cpuprofile
  • 高内存占用:heapsnapshot
  • 性能分析

部署

部署需要解决的问题

  • 守护进程:当进程退出时,重新拉起
  • 多进程:cluster 便捷地利用多进程
  • 记录进程状态,用于诊断

容器环境

  • 通常有健康检查的手段,只需考虑多核CPU利用率即可

延伸话题

Node.js 贡献代码

快速了解Node.js代码

  • 从使用者的角色逐步理解底层细节,可以解决更复杂的问题
  • 自我证明,有助于职业发展
  • 解决社区问题,促进社区发展

编译Node.js

认知:黑盒到白盒,发生问题时能有迹可循

贡献代码的第一步

三、课后个人总结:

今天学习了Node.js,了解了Node.js的大量应用场景及其强大的能力,还有Node.js的运行结构。还有非常充实饱满的Live Coding环节,老师带着我们编写了许多实用的小例子,让我们快速上手。还谈了一些延伸话题,启发我们学习的方向的动力。