Node.js学习 | 青训营笔记

116 阅读3分钟

Node.js学习 | 青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

今天学习了node.js的部分知识,在此记录一下

介绍:

  1. node.js的应用场景
  2. node.js运行时结构
  3. 编写HTTP server

01:node.js的应用场景

  1. 前端工程化(例如打包工具,Babel,esbuild等)
  2. web服务器应用
  3. Electron跨端桌面应用

前端工程化

  • Bundle(打包工具):webpack, vite, esbuild, parcel
  • Uglify(代码压缩工具):uglifyjs
  • Transpile(语法转换工具):bablejs,typescript
  • 其他语言加入:esbuild(go),parcel(rust),prisma

web服务器应用

  • 学习曲线平缓,开发效率高
  • 运行效率接近常见的编译语言
  • 社区生态丰富,工具链成熟(npm,v8 inspector)
  • 与前端结合的场景会有优势(ssr服务端渲染)

Electron跨端桌面应用(window,linux,mac端)

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

node.js在字节

  • BFF应用、SSR应用,例如:Modern.js
  • 服务端应用:头条搜索,西瓜视频,懂车帝
  • electron:飞书,飞连

02:Node.js运行结构

Node.js运行时结构-V8,libuv

  • V8: JavaScript Runtime,诊断调试工具(inspector)
  • libuv: eventloop (事件循环,封装各种操作系统api,跨平台io操作),syscall (系统调用)
  • 举例:用node-fetch发起请求时

特点

  1. 异步I/O
  2. 单线程
  3. 跨平台

异步I/O

当Node.js执行l/Q操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待

单线程

  • JS单线程

●实际:JS线程+uv线程池+V8任务线程池+V8Inspector线程

  • 优点:不用考虑多线程状态同步问题,也就不需要锁;同时还能比较高效地利用系统资源;
  • 缺点:阻塞会产生更多负面影响

    ●解决办法:多进程或多线程

跨平台

  • 跨平台(大部分功能、api)

  • Node.js跨平台+ JS无需编译环境(+ Web跨平台+诊断工具跨平台)

    =开发成本低(大部分场景无需担心跨平台问题),整体学习成本低

03:编写Http Server

本节目标

  1. 安装Node.js
  2. 编写Http Server + Client, 收发GET, POST请求
  3. 编写静态文件服务器
  4. 编写React SSR服务
  5. 适用inspector进行调试、诊断
  6. 部署简介

安装Node.js

  • Mac, Linux推荐使用nvm。多版本管理。
  • Windows推荐nvm4w或是官方安装包。
  • 安装慢,安装失败的情况,设置安装源
  • NVM_NODEJS_ORG_MIRROR=nommirror.com/mirrors/nod… nvm install 16

编写Http Server + Client, 收发GET, POST请求

promise + await async实现

const http = require('http')
const server = http.createServer(async (req,res)=>{
    //receive body through promise
    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'
            try {
                const ret = JSON.parse(buf)
                msg = ret.msg
            } catch (error) {
                //
            }
            resolve(msg)
        })
    })
    //RESPINSE
    const responseJson = {
        msg:`receive:${msg}`
    }
    console.log(JSON.stringify(responseJson))
    res.setHeader('Content-Type','application/json')
    res.end(JSON.stringify(responseJson))
})
const port = 3000
server.listen(port,()=>{
    console.log("serve start")
})

http_client.js

const http = require('http')
const body = JSON.stringify({
    msg: 'hello from my client'
})
const req = http.request('http://127.0.0.1:3000',{
    method:'POST',
    headers:{
        'Content-Type':'application/json',
    },
},(res)=>{
    const bufs=[]
    res.on('data',data=>{
        bufs.push(data)
    })
    res.on('end',()=>{
        const receive = JSON.parse(Buffer.concat(bufs))
        console.log('json.msg is:',receive.msg)
    })
})
req.end(body)

编写静态文件服务器

与高性能、可靠的服务相比,还差什么? 外部服务: cloudflare, 七牛云,阿里云,火山云.

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

课程代码

//编写静态文件服务
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)=>{
    const info = url.parse(req.url)
    console.log("info_path",info.path)
    const filePath = path.resolve(folderPath,'./'+info.path)
    console.log(filePath)
    const filestream = fs.createReadStream(filePath)
    filestream.pipe(res)
})
server.listen(port,()=>{
    console.log("serve start")
})

编写React SSR服务

  • SSR (server side rendering)有什么特点

    • 相比传统HTML模版引擎:避免重复编写代码

    • 相比SPA (single page application) : 首屏渲染更快,SEO (搜索引擎优化)友好

    • 缺点:

      • 通常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 serve = http.createServer((req,res)=>{
    res.sendDate(`
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
        </head>
        <body>
            ${ReactDOMServer.renderToString(
                React.createElement(App,{},'my_content')
            )}
            <script>
                //init react application..
            </script>
        </body>
        </html>
    `)
})
const port = 3000
server.listen(port,()=>{
    console.log(`server listens on:${port}`)
})

SSR难点:

  1. 需要处理打包代码

  2. 需要考虑前端的代码在服务器运行时的逻辑

    1. 一般放在react生命周期后面
  3. 移除对服务端无意义的副作用或者重置环境

适用inspector进行调试、诊断

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

    • node --inspect
  • 场景:

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

部署简介

  • 部署要解决的问题

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

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