乐趣区

关于前端:Nodejs-基础笔记

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 利用率即可
退出移动版