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

77 阅读4分钟

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

Node.js的应用场景

前端工程化

打包工具:webpack,vite,esbuild,parcel

压缩:uglifyjs

转义:babeljs,typescript

其他语言的加入:esbuild,parcel,prisma

web服务端应用

特点:

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

Electron跨端桌面应用

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

Node.js运行时结构

image.png

  • 用户安装的依赖以及代码都是存放在用户代码
  • node-inspect诊断调试工具
  • N-API是用于某个场景或需求使用js效率较低,其中会更原生的语言来实现功能
  • libuv封装了各种操作系统的API
  • nghtt2封装关于http2相关的模块和node核心eventloop(事件循环)
  • llhttp做http的解析
  • zlib做一些常见的压缩和解压缩的算法
  • c-ares用于做dns查询的库
  • OpenSSL网络层面的加密解密

举例 node-fetch发起请求的调用

安装

通过npm安装node-fetch,那么对应的代码就会存储在用户代码

发起请求

我们在代码中调用node-fetch,那么会到v8中执行,该模块在底层中调用的是Node.js Core(javascript),然后调用更底层的Node.js Core(C++),在其中又使用到llhttp 序列化与反序列化,然后通过libuv创建tcp链接,把数据发送给远端

接收数据

拿到数据之后,经过llhttp解析,经过Node.js Core(javascript)代码,再到用户的代码

特点

  1. 异步 I/O

image.png 2. 单线程 只有JavaScript是单线程

实际上:JS线程 + uv线程池 + V8任务线程池 + V8 Inspector线程

优点:不用考虑多线程状态同步问题,也就不需要锁;同时还能比较高效地利用系统资源

缺点:阻塞会产生更多负面影响

解决方法:多进程或多线程

3. 跨平台

image.png

编写HTTP server

Http Server

const http = require('http')

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

const port = 3000

server.listen(port, () => {
    console.log('listening on',port)
})

接收并传递JSON数据

const http = require('http')

const server = http.createServer((req,res) => {
    const bufs = []
    // 接收body数据
    req.on('data', (buf) => {
        bufs.push(buf)
    })

    req.on('end', () => {
        const buf = Buffer.concat(bufs).toString('utf-8')
        let msg = 'hello'

        try{
            const ret = JSON.parse(buf)
            msg = ret.msg
        } catch(error) {
            //
        }

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

发送请求

const http = require('http')

const body = JSON.stringify({
    msg:"hello from own 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 server = http.createServer((req, res) => {
  // expected http://127.0.0.1:3000/index.html?abc=10
  const info = url.parse(req.url);
  console.log("info", info);
  // static/index.html
  const filePath = path.resolve(folderPath, "./" + info.pathname);
  console.log("filePath", filePath);
  const filestream = fs.createReadStream(filePath);

  filestream.pipe(res);
});

const port = 3000;
server.listen(port, () => {
  console.log("listening on", port);
});

运用stream的好处:

把所有数据都存储在stream中,按需返回给客户端,内存使用率较高

与高性能、可靠的服务相比,还差什么:

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

React SSR

SSR特点:

  • 相比传统HTML模型引擎:避免传统编写代码
  • 相比SPA:首屏渲染更快,SEO友好

缺点:通常qps较低,前端代码编写时需要考虑服务端渲染情况

image.png

image.png

Debug

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

场景:

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

比如启动的文件名称是ssr_example.js,那么可输入命令行

node --inspect ssr_example.js

image.png

通过一个链接,可能会出现以下的情况,它期望连接websocket

image.png

我们可以进入http://127.0.0.1:9229/json,跳转到红色框内的url

image.png

console:查看打印的信息

sources:调试断点

memory:调试内存问题

profilter:可以查看不同时间段的函数执行,可以用来查找高循环等问题

image.png

部署

部署需要解决的问题:

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

容器环境:通常有健康检查的手段,只需考虑多核cpu利用率即可