NodeJS与前端开发实战 | 青训营笔记

159 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的的第十五天。 今天学习了《NodeJS与前端开发实战》,跟着老师的讲解,从NodeJs的应用到结构,再到手动搭建一个简单的HttpServer,让我对NodeJs的理解更加深入了一步,Node.js是一个专注于实现高性能Web服务器优化的专家,几经探索,几经挫折后,遇到V8而诞生的项目。Node.js是一个让JavaScript运行在服务器端的开发平台 ,所以,不管现在还是以后工作,NodeJs都是一大利器。

本堂课重点内容

  • NodeJS的应用场景
  • NodeJS的运行结构
  • 搭建HttpServer

详细知识点介绍

详细知识点总结在文章的最后,坚持打卡,学习!

课后个人总结

对于NodeJS,我的理解就是可以运行在后端的JavaScript。 为什么它能够在后端运行呢? 这就得归功于V8引擎(V8是Google Chrome浏览器的JavaScript引擎),通过对高性能V8引擎的封装,并通过一系列优化的API类库,使其就能够在后端运行了。

NodeJS可以让我们脱离后端自己编写所需要的接口,但是这并不代表我们以后都不需要后端了。Node对于i/o流的操作,在目前的主流后端语言中数一数二。基于这种特性,很多公司都是用NodeJS做项目搭建工具,这就说明我们学习NodeJS是必要的。

笔记

NodeJS与前端开发实战

1、NodeJs的应用场景

  • 前端工程化
  • Web服务端应用
  • Electron跨桌面应用

(1)前端工程化

  • Bundle: webpack, vite, esbuild, parcel
  • Uglify: uglifyjs
  • Transpile: bablejs,typescript
  • 其他语言加入竞争:esbuild, parcel, prisma
  • 现状:难以替代

(2)Web服务端应用

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

(3)Electron跨桌面应用

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

2、NodeJS运行时结构

image-20220808173700850.png

[NodeJS运行时结构]  github.com/joyeecheung… 

(1)运行时结构

  • V8:JavaScript Runtime,诊断调试工具(inspector)
  • libuv: eventloop(事件循环),syscall(系统调用)

(2)特点

  • 异步 I / O
  • 单线程
  • 跨平台

① 异步 I / O

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

image-20220808181610848.png

② 单线程

JS单线程

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

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

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

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

③ 跨平台

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

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

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

3、编写Http Server

  • 安装NodeJS
  • 编写Http Server +Client,收发GET,POST 请求
  • 编写静态文件服务器
  • 编写React SSR 服务
  • 适用inspector进行调试、诊断
  • 部署简介

(1)安装NodeJS

  • Mac,Linux推荐使用nvm。多版本管理。
  • Windows推荐nvm4w 或是官方安装包。
  • 安装慢,安装失败的情况,设置安装源。

(2)编写HttpServer

① http_server.js

const http = require('http')
​
const port = 3000const server = http.createServer((req, res) => {
    res.end('hello')
})
​
server.listen(port, () => {
    console.log('listening on', port)
})

② http_server_json.js

const http = require('http')
​
const server = http.createServer((req, res) => {
    const bufs = [];
    req.on('data', data => {
        bufs.push(data);
    })
    req.on('end', () => {
        const buf = Buffer.concat(bufs).toString('utf-8')
        let msg ='hello'
        try {
            const ret = JSON.parse(buf)
            msg = ret.msg 
        } catch (err) {
            
        }
        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)
})

③ http_client.js

const http = require('http')
​
const server = http.createServer((req, res) => {
    const bufs = [];
    req.on('data', data => {
        bufs.push(data);
    })
    req.on('end', () => {
        const buf = Buffer.concat(bufs).toString('utf-8')
        let msg ='hello'
        try {
            const ret = JSON.parse(buf)
            msg = ret.msg 
        } catch (err) {
            
        }
        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)
})

④ Promisify

用Promise + async await 重写这两个例子(why)

技巧:将callback 转换成promise

http_server_json.js

const http = require('http')
​
const server = http.createServer(async (req, res) => {
    // receive body from client
    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('utf-8')
            let msg = 'hello'
            try {
                const ret = JSON.parse(buf)
                msg = ret.msg
            } catch (err) {
            }
​
            resolve(msg)
        })
    })
​
​
    // response
    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)
})

(3)静态文件

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

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
    const info = url.parse(req.url)
    // static/index.html 
    const filePath = path.resolve(folderPath, './' + info.path)
    //stream api
    const fileStream =  fs.createReadStream(filePath)
    fileStream.pipe(res)
})
​
const port = 3000
​
server.listen(port, () => {
    console.log('listening on', port)
})

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

1、CDN:缓存 + 加速

2、分布式存储,容灾

(4)React SSR

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

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

  • 相比SPA (single page application):首屏渲染更快,SEO友好

  • 缺点:

    • 通常qps较低,前端代码编写时需要考虑服务端渲染情况
const http = require('http')
​
const port = 3000const server = http.createServer((req, res) => {
    res.setHeader(' Content-Type ', 'text/html')
    res.end(`
        <DOCTYPE html><html>
            <head>
                <title>My App</title>
            </head>
        </html>
            <body>
                <h1>My App</h1>
            </body>
        </html>`)
})
server.listen(port, () => {
    console.log('listening on', port)
})

SSR难点:

  • 1、需要处理打包代码
  • 2、需要思考前端代码在服务端运行时的逻辑
  • 3、移除对服务端无意义的副作用,或重置环境

(5)Debug调试

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

  • node --inspect
  • open http:/ /localhost:9229/json

场景:

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

(6)部署

  • 部署要解决的问题

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

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