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

314 阅读5分钟

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

课堂笔记

本堂课重点内容:

  1. 介绍 Node.js 的应用场景
  2. 介绍 Node.js 运行时结构
  3. 如何用编写 Http Server
  4. 延伸话题

课前

安装 Node.js。可以选择以下一种方式:

  1. 从 Node.js 官网安装 nodejs.org/en/
  2. Mac, Linux 环境可以使用 nvm 进行安装 github.com/nvm-sh/nvm
  • NVM_NODEJS_ORG_MIRROR=npmmirror.com/mirrors/nod… nvm install 16

详细知识点介绍

01. Node.js 的应用场景(why)

image.png

  • 首先要提到的是前端工程化的场景。早期ajax,jquery比较流行的时候,我们直接在页面中引入需要的ib即可。后续随着模块化、transpile逐渐成熟、需求逐渐增多,对后端能力的需求也逐渐强烈。反过来说,也正是Node.js赋予了js开发者在浏览器外运行代码的能力,加速催生了这些项目的出现。

  • 第二个常见的场景是使用Node,js开发Web服务端应用,也就是后端服务。这里我们特别提一下vercel这家公司。

  • 最后要再说下Electron应用,这里不只是指electron,还包括nw.js.;这个场景在较大的企业里面非常常见。

第一个场景:前端工程化

  • 用于打包的工具Bundle: webpack, vite, esbuild, parcel
  • 用于压缩的工具Uglify: uglify.js
  • 用于语法的转换Transpile: bable.js, typescript
  • 其他语言加入竟争:基于go的esbuild, parcel, prisma
  • 现状:Nodejs难以替代

第二个场景:Web服务器应用

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

第三个场景:Electron跨端桌面应用

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

第四个场景:Node.js在字节

image.png

  • BFF应用(作为端的开发者,需要很多服务端的接口,谁需要接口谁去开发,后端的开发者不需要跟前端紧密结合)、SSR应用,举例:字节开源的框架Modern.js
  • 服务端应用,举例:头条搜索,西瓜视频,懂车帝(PC站点用的nodejs)
  • Electron应用:飞连,飞书(迭代更新)
  • 每年新增1000+ Node.js应用

02. Node.js 运行时结构(what)

image.png nodejs的组成图引用自:github.com/joyeecheung… and learn 2019 beijing/contributing-to-node-core.pdf

  • 用户代码就是业务代码,
  • nodejs里面很多都是JavaScript写的。
  • 当用JavaScript解决不了时效率比较低,用一种更native的N-API是特殊的场景。底层有很多c++的代码。
  • v8引擎:JavaScript Runtime,诊断调试工具(inspector)
  • libuv:封装了各种操作系统API,还提供了nodejs里面核心的eventloop(事件循环),syscall(系统调用)
  • zlib:做一些常见的压缩和解压缩
  • c-ares:用来做DNS查询的库
  • llhttp:做http的解析
  • openssl:网络层面的加密解密工具
  • 举例:用node-fetch发起请求时

nodejs结构的特点;

1. 异步IO

image.png

image.png 当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待,有更多内存可用,系统资源利用率比较高。

2. 单线程:

image.png worker thread可以起独立线程,但每个线程的模型没有太大变化

  • JS单线程----实际:JS线程+uv线程池+V8任务线程池+V8 Inspector线程

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

  • 缺点:阻塞会产生更多负面影响-----解决办法:多进程或多线程

3. 跨平台

image.png

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

03. 编写 Http Server(how)

本节目标:

1. 安装nodejs

Mac,Linux推荐使用nvm。多版本管理。

Windows推荐nvm4w或是官方安装包。

安装慢,安装失败的情况,设置安装源

NVM_NODEJS_ORG_MIRROR=npmmirror.com/mirrors/nod… nvm install 16

推荐官方安装包

2. 编写http server+client,收发GET,POST请求

http server image.png Hello word

const http = require('http')
const port = 3000
const server = http.createServer((req,res)=>{
    res.end('hello world')
})
server.lisen(port,()=>{
    console.log('server listens on:${port}`)
}}

JSON {"echo'":"Hello"}

const server = http.createServer((req,res)=>{
    const bufs=[]
    req..on('data',data=>{
    bufs.push(data)
    })
    req.on('end',{}=>{
        let reqData = {}
        try{
            reqData = JSON.parse(Buffer.concat(bufs).toString())
          }catch(err){
          }
          res.setHead('Content-type','application/json')
          res.end(JSON.stringify({
              echo:reqData.msg || 'Hello',
          }))
    }
})

http client

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",
    header:{
    'Content-type':'application/json',
    'Content-Length':body.length,
    },
},(res)=>{
    const bufs = []
    res.on('data',data =>{
        bufs.push(data)
    })
    res.on('end',()=>{
        const receive = JSON.parse(Buffer.concat(bufs(.toString())
        console.log('receive:',receive
        
    })
})
req.end(body)

promisify

  • 用Promise+async await重写这两个例子(why)
  • 技巧:将callback转换成promise image.png
3. 编写静态文件服务器

编写一个简单的静态文件服务

const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');

const port = 3000
const server = http.createServer((req,res)=>{
    const info = url.parse(req.url)
    const file = fs.createReadStream(path.resolve(_dirname,'.'+info.pathname))
    file.pipe(res)
})
server.listen(port,()=>{
    console.log('server listens on:${port}')
})

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

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

外部服务:cloudflare,七牛云,阿里云,火山云(字节跳动)...

4. 编写react SSR服务

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

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

HTML例子:

image.png

image.png

替换成React:

image.png

SSR难点:

  1. 需要处理打包代码 image.png Js tmp.js require('./static/style.css')

  2. 需要思考前端代码在服务端运行时的逻辑 image.png async componentDidMount() const res=await fetch('my.server.domain')

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

5. 适用inspector进行debug、诊断

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

场景:

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

演示 image.png

image.png

image.png

6. 部署简介

部署要解决的问题

  • 守护进程:当进程退出时,重新拉起
  • 多进程:cluster便捷地利用多进程
  • 记录进程状态,用于诊断 容器环境
  • 通常有健康检查的手段,只需考虑多核cpu利用率即可

04. 延伸话题

nodejs贡献代码

快速了解Node.js代码

好处:

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

编译nodejs

为什么要学习编译Node.js

  • 认知:黑盒到白盒,发生问题时能有迹可循
  • 贡献代码的第一步 如何编译
  • 参考:Maintaining the build files
  • /configure &&make install
  • 演示:给net模块添加自定义属性

课后

  • 了解并尝试使用更多 Node.js 的原生模块 nodejs.org/dist/latest…
  • 学习在 npm 上搜索并安装模块 www.npmjs.com/