Node.js 的利用场景

  • 前端工程化

    • 打包工具(bundle):webpack、vite、esbuild、parcel
    • 代码压缩(uglify):uglifyjs
    • 语法转换(transpile):bablejs、typescript
    • 其余语言退出竞争:esbuild(golang)、parcel(rust)、prisma
    • Node.js 现状:在前端工程化畛域依然难以代替
  • Web 服务端利用

    • 学习曲线平缓,开发效率较高
    • 运行效率靠近常见的编译语言
    • 社区生态丰盛以及工具链成熟(npm、V8 inspector)
    • 与前端联合的场景会有劣势(服务端渲染ssr)
    • 现状:竞争强烈,Node.js 有本人独特的劣势
  • Electron 跨端桌面利用

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

    • BFF(Backends For Frontends)利用:作为中间件自在解决后端接口,使得前后端开发更加拆散
    • SSR(Server Side Render)利用:将组件或页面通过服务器生成 HTML 字符串,再发送到浏览器

Node.js 运行时构造

  • Node.js 组成

    • V8:JavaScript Runtime,诊断调试工具(inspector)
    • libuv:eventLoop(事件循环),syscall(零碎调用)
    • Node.js 运行示例(以 node-fetch 为例):
  • Node.js 运行时构造特点

    • 异步 I/O

      • 当 Node.js 执行 I/O 操作时,会在响应返回后复原操作,而不是阻塞线程并占用额定内存期待
    • 单线程

      • JavaScript 是单线程的(只有一个主线程),但理论在 Node.js 环境中是:JavaScript 单线程 + libuv 线程池 + V8 工作线程池 + V8 inspector 线程
      • 比方读取文件(常见的I/O操作)时,将该工作交给 libuv 线程池去操作,JS 主线程便能够进行其余工作
      • V8 inspector 独自作为一个线程的作用:比方写了一个死循环,惯例状况无奈再去调试,便能够利用V8 inspector 线程调试该死循环
      • 长处:不必思考多线程状态同步问题,也就不须要锁,同时还能比拟高效地利用系统资源
      • 毛病:阻塞会产生更多负面影响,解决办法:多过程或多线程
    • 跨平台

      • Node.js 跨平台 + JS 无需编译环境 + Web 跨平台 + 诊断工具跨平台
      • 长处:开发成本低,整体学习成本低

编写 HTTP Server

  • 编写一个简略的 server 服务,使得浏览器关上主机的3000端口看到 hello!nodejs!

    const http = require('http');const port = 3000;const server = http.createServer((req, res) => {    res.end('hello!nodejs!');})server.listen(port, () => {    console.log('success!', port);});
  • 把用户申请中的数据转换为 JSON 格局,并在响应中返回给浏览器

    const http = require('http');const port = 3000;const server = http.createServer((req, res) => {    const bufs = [];    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 (err) {            //这里申请数据不非法临时不做解决(因为无奈发送非法数据),msg的值仍是hello        }        // 解决响应         const responseJson = {            msg: `receive : ${msg}`,        }        // 响应头设置Content-Type        res.setHeader('Content-Type', 'application/json');        res.end(JSON.stringify(responseJson));    });});server.listen(port, () => {    console.log('success!', port);});
  • 搭建一个客户端,能给server发送post申请

    const http = require('http');const port = 3000;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) => {    // 拿到响应当前,转换为JSON格局,输入在控制台    const bufs = [];    res.on('data', (buf) => {        bufs.push(buf);    })    res.on('end', () => {        const buf = Buffer.concat(bufs);        const res = JSON.parse(buf);        console.log('json.msg is : ', res);    })});// 发送申请req.end(body);
  • 用 promise + async await 重写这两个例子

    const http = require('http');const port = 3000;const server = http.createServer(async (req, res) => {    // recieve body from client    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('utf-8');            let msg = 'hello!nodejs!'            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));});server.listen(port, () => {    console.log('success!', port);});
  • 搭建一个能够读取服务器上动态资源的 server

    const http = require('http');const fs = require('fs');const url = require('url');const path = require('path');// 文件门路const folderPath = path.resolve(__dirname, './static_server');const server = http.createServer((req, res) => {    // expected http://127.0.0.1:3000/index.html?abc=10    const info = url.parse(req.url);    // static_server.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);})
    • 与高性能、牢靠的动态文件服务器相比,还须要缓存、减速、分布式缓存的能力
  • SSR(server side render)有什么特点

    • 相比传统 HTML 模板引擎防止了反复编写代码
    • 相比SPA(single page application)首屏渲染更快,SEO(搜索引擎敌对 Search Engine Optimization)敌对
    • 毛病:通常 qps(每秒查问率 Queries-per-second) 较低,前端代码编写时须要思考服务端渲染状况
  • 编写 React-SSR HTTP Server

    const React = require('react');const ReactDOMServer = require('react-dom/server');const http = require('http');const App = (props) => {    // 防止编译问题不应用 jsx    return React.createElement('div', {}, props.children || 'Hello!')}const port = 3000;const server = http.createServer((req, res) => {    res.end(`        <!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>my application</title>        </head>        <body>            ${ReactDOMServer.renderToString(              React.createElement(App, {}, 'my_content')            )}            <script>              init......            </script>        </body >        </html >    `);})server.listen(port, () => {    console.log('success!', port);});
  • SSR 难点:

    • 须要解决打包代码
    • 须要思考前端代码在服务端运行时的逻辑
    • 移除对服务端毫无意义的副作用或重置环境
  • HTTP Server -Debug

    • 运行js文件时,在文件名之前加上 --inspector 指令,在浏览器输出对应的调试地址,查看 json 信息能够跳转到前端调试界面。以static_file_server为例,输出127.0.0.1:9229/json即可
    • 理论开发环境中打断点调试会比拟危险,能够打 logpoint
    • Memory 面板:能够打一个 snapshot 查看 JavaScript 主线程中的内存占用信息
    • Profile 面板:能够录制一个 profile 查看 CPU 一段时间内的运行状况,能够看到调用了哪些函数。比方利用产生了死循环,能够通过 profile 查看死循环时在运行什么代码
    • 也能够通过上述数据察看代码性能,判断函数运行工夫是否合乎预期
  • HTTP Server -部署

    • 部署要解决的问题

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

      • 通常有健康检查的伎俩,只须要思考多核 CPU 利用率即可