重学Node.js及其框架(Express, Koa, egg.js) 之 Nodejs基础(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 重学Node.js及其框架(Express, Koa, egg.js) 之 Nodejs基础

event模块


  • on,监听事件


  • emit,发送事件


  • off,移除事件。需要指定事件名和事件


  • once,只执行一次


  • removeAllListeners():删除全部事件,或者传入事件名来移除指定事件。


  • eventNames()获取全部事件名。


  • listenerCount(事件名)获取当前事件注册的个数。


  • listeners(事件名)获取当前事件数组。


const EventEmitter = require("events");
    const events = new EventEmitter();
    // 监听事件
    events.addListener("zhanghao", (...args) => {
      console.log("addListener监听", ...args)
    })
    const listener1 = (...args) => {
      console.log("on监听", ...args)
    }
    events.addListener("llm", listener1)
    // events.off("llm", listener1)
    // 注册事件
    events.removeAllListeners("llm")
    events.emit("zhanghao", "我的")
    events.emit("llm", "他的")
    console.log(events.listenerCount('zhanghao'))
    console.log(events.listeners('llm'))


Buffer


创建buffer的常用方式


  • from


// 编码和解码的方式不同,解码会出现乱码。
    const buf = Buffer.from("中国加油", 'utf16le');
    // 对于utf-8编码的汉字而言,一个汉字等于3字节,对于utf16le编码的汉字而言,一个汉字等于2字节
    console.log(buf)
    console.log(buf.toString("utf16le"))// 指定编码,不然默认是utf-8编码,会乱码。


  • alloc(指定创建多大的缓存区)


// buf就相当于一个字节数组。没有给数组赋值时,他就相当于都是0000000...占位
    const buf = Buffer.alloc(10);
    console.log(buf) //<Buffer 00 00 00 00 00 00 00 00 00 00>
    // 对buffer赋值
    buf[0] = 0x90; // 为了方便展示,输出的都是16进制来表示二进制。4位等于一个16进制数。所以2个16进制数表示1字节
    console.log(buf)// <Buffer 90 00 00 00 00 00 00 00 00 00>


读取二进制文件(视频,图片等非文本文件)并对其做一些事情


我们可以通过sharp库或者jimp库来对图片进行处理


github.com/lovell/shar…


www.npmjs.com/package/jim…


// 将图片做简单的变化,然后输出
    const sharp = require("sharp");
    sharp('./sun.jpg')
      .resize(200, 200)
      .toFile("sunCopy.jpg")


stream流


什么是流


程序中的流也是类似的含义,我们可以想象当我们从一个文件中读取数据时,文件的二进制(字节)数据会源源不断的被读取到我们程序中。而这个一连串的字节,就是我们程序中的流。所有的流都是EventEmitter的实例


在之前学习文件的读写时,我们可以直接通过 readFile或者 writeFile方式读写文件,为什么还需要流呢?


  • 直接读写文件的方式,虽然简单,但是无法控制一些细节的操作。比如从什么位置开始读、读到什么位置、一次性读取多少个字节;


  • 读到某个位置后,暂停读取,某个时刻恢复读取等等;


  • 或者这个文件非常大,比如一个视频文件,一次性全部读取并不合适;


Node.js中有四种基本流类型:


  • Writable:可以向其写入数据的流(例如 fs.createWriteStream())。


  • Readable:可以从中读取数据的流(例如 fs.createReadStream())。


  • Duplex:同时为Readable和的流Writable(例如 net.Socket)。


  • Transform:Duplex可以在写入和读取数据时修改或转换数据的流(例如zlib.createDeflate())。 下面我们来介绍一下可读流和可写流:


Readable可读流


以前通过fs.readFile()来读取文件,是一次性将一个文件中所有的内容都读取到程序(内存)中。并且不能指定读取位置。所以我们就需要通过可读流

fs.createReadStream(path, options)来读取文件。读取完毕后文件自动关闭其中options就是来对读取文件做约束的。


  • start:开始读取的位置(从0开始的)


  • end:结束的位置


  • highWaterMark:文件一次性读取多少字节。默认64kb


  • encoding: 编码格式


  • flags: 文件标识。默认为r


  • ...


const fs = require("fs");
    const read = fs.createReadStream("./zh.txt", {
      // 位置是从0开始的
      start: 3,
      end: 8,
      highWaterMark: 2, //文件一次性读取多少字节。默认64kb
      encoding: 'utf-8'
    })
    // 可以通过pause(),resume()方法对文件读取进行暂停和恢复。
    read.on("data", (data) => {
      console.log(data)
    })
    read.on("open", () => {
      console.log("文件打开")
    })
    read.on("close", () => {
      console.log("文件关闭")
    })
    read.on("end", () => {
      console.log("文件读取完毕")
    })


Writable可写流


以前通过fs.writeFile()来写入文件,而且只能是将源文件覆盖或者追加到源文件后面。所以我们就需要通过可读流fs.createWriteStream(path, options)来精确地写入文件。


其中options就是来对写入文件做约束的。


  • start:开始读取的位置(从0开始的)


  • encoding: 编码格式


  • flags: 文件标识。默认为w


const fs = require("fs");
    const writeStream = fs.createWriteStream("./zh.txt", {
      flags: 'r+', // 这里可能会出现bug,应该需要指定为r+。
      encoding: 'utf-8',
      start: 4
    })
    // 写入时会覆盖原来位置的文字。
    writeStream.write("0000", (err) => {
      console.log(err)
    })
    // end方法既可以写入东西,并且也会自动关闭文件。
    writeStream.end("文件写入的内容")
    writeStream.on("open", () => {
      console.log("文件即将写入")
    })
    // writeStream.close()
    writeStream.on("close", () => {
      console.log("文件关闭")
    })


pipe


连接可写流和可读流。


可读流 =====pipe=====> 可写流


const fs = require('fs');
    const reader = fs.createReadStream("./zh.txt");
    const writer = fs.createWriteStream("./llm.txt");
    // pipe管道会自动关闭。不需要我们手动调用close事件。
    reader.pipe(writer)
    writer.on("close", () => {
      console.log("文件将要关闭")
    })


http模块


什么是Web服务器?


当应用程序(客户端)需要某一个资源时,可以向一个台服务器,通过Http请求获取到这个资源;提供资源的这个服务器,就是一个Web服务器;


创建服务器


const http = require("http");
    // 方式一
    const server = new http.Server((req, res) => {
      res.writeHead(200, {
        'content-type': 'text/html;charset:ASCII'
      })
      res.end("<h1>我是一个汉字,当编码格式是ascii时,看其乱不乱码</h1>")//会乱码。需要设置charset:utf-8格式。
    })
    server.listen(8080, () => {
      console.log("服务器创建成功")
    })
    // 方式二,这种方式底层其实也是调用Server类。
    http.createServer((req, res) => {
      // 指定body中数据的形式
      // req.setEncoding("utf-8")
      req.setEncoding("binary")
      req.on("data", (data) => {
        console.log("data", typeof data) //string
        res.end(data)
      })
      // res.end("返回数据")
    }).listen(3030, () => {
      console.log("服务器启动在3030端口")
    })


Server通过listen方法来开启服务器,并且在某一个主机和端口上监听网络请求:

listen函数有三个参数:


  • 端口port: 可以不传, 系统会默认分配端, 可以通过server.address().port拿到端口号,后续项目中我们会写入到环境变量中;


  • 主机host: 通常可以传入localhost、ip地址127.0.0.1、或者ip地址0.0.0.0,默认是0.0.0.0;


  • localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;


  • 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;正常的数据包经过应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过主机ip地址是不能访问的;


  • 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;回调函数:服务器启动成功时的回调函数;


解析query参数


我们可以通过url模块解析req.url取出query。然后在通过querystring模块解析query字段生成一个对象。


const url = require("url")
const queryString = require("querystring")
const {query} = url.parse(req.url);
const queryObj = queryString.parse(query);


url解析


const url = require("url");
    console.log(url.parse("http://127.0.0.1/user/1?name=zh&age=20#llm"))


网络异常,图片无法展示
|


解析请求体中的参数


由于request是流。所以我们读取post请求传入的数据,我们需要通过监听data事件来读取,然后再将二进制流转为字符串。然后在通过JSON.parse()解析字符串。


let dataBody = ""
      // 设置body的编码
      req.setEncoding("utf-8")
      req.on("data", (data) => {
        dataBody += data; // 这里拼接是为了当一次输入类型不能完全获取,需要多次拼接,才能获取完整数据。
      })
      req.on("close", () => {
        // 当文件关闭之前,解析参数
        const requestData = JSON.parse(dataBody)
        console.log(requestData)
      })


请求和响应的一些字段


请求头字段


content-type: 指定传入的数据的类型。请求体类型


  • application/json表示是一个json类型;


  • text/plain表示是文本类型;


  • application/xml表示是xml类型;


  • multipart/form-data表示是上传文件;


content-length: 传递的数据长度。


accept-encoing:告诉服务器,客户端支持的文件压缩格式。


accept: 告诉服务器,客户端可以接受文件的格式类型。


响应response


只能通过res.end()方法来结束响应。并且返回响应。


Write方法:这种方式是直接写出数据,但是并没有关闭流。 设置状态码:


  • res.statusCode = “”。


  • res.writeHead("状态码", {响应头})


设置响应头


  • res.setHeader()


注意:text/html表示响应字段返回的是一个html格式的,可以直接渲染在页面中,但是application/html,表示下载html文件。


http中发送网络请求


回调函数的参数类型和http开启一个服务中的request参数类型是一样的,都是incomingMessage。所以需要通过监听data事件来获取结果。


  • 发送get请求


const http = require('http');
    // http发送get请求
    http.get('http://localhost:8888', (res) => {
      res.on('data', (data) => {
        console.log(data.toString());
      });
      res.on('end', () => {
        console.log("获取到了所有的结果");
      })
    })


  • 发送post请求。注意这里需要调用end方法,告诉服务器请求结束,否则会发生堵塞。


const req = http.request({
      method: 'POST',
      hostname: 'localhost',
      port: 8888
    }, (res) => {
      res.on('data', (data) => {
        console.log(data.toString());
      });
      res.on('end', () => {
        console.log("获取到了所有的结果");
      })
    });
    req.end();


但是一般使用axios库来发送网络请求。


相关文章
|
1月前
|
缓存 负载均衡 JavaScript
构建高效后端服务:Node.js与Express框架实践
在数字化时代的浪潮中,后端服务的重要性不言而喻。本文将通过深入浅出的方式介绍如何利用Node.js及其强大的Express框架来搭建一个高效的后端服务。我们将从零开始,逐步深入,不仅涉及基础的代码编写,更会探讨如何优化性能和处理高并发场景。无论你是后端新手还是希望提高现有技能的开发者,这篇文章都将为你提供宝贵的知识和启示。
|
1月前
|
JavaScript
使用node.js搭建一个express后端服务器
Express 是 Node.js 的一个库,用于搭建后端服务器。本文将指导你从零开始构建一个简易的 Express 服务器,包括项目初始化、代码编写、服务启动与项目结构优化。通过创建 handler 和 router 文件夹分离路由和处理逻辑,使项目更清晰易维护。最后,通过 Postman 测试确保服务正常运行。
68 1
|
1月前
|
Web App开发 JSON JavaScript
Node.js 中的中间件机制与 Express 应用
Node.js 中的中间件机制与 Express 应用
|
1月前
|
Web App开发 JavaScript 前端开发
探索后端开发:Node.js与Express的完美结合
【10月更文挑战第33天】本文将带领读者深入了解Node.js和Express的强强联手,通过实际案例揭示它们如何简化后端开发流程,提升应用性能。我们将一起探索这两个技术的核心概念、优势以及它们如何共同作用于现代Web开发中。准备好,让我们一起开启这场技术之旅!
49 0
|
1月前
|
Web App开发 JavaScript 前端开发
构建高效后端服务:Node.js与Express框架的实践
【10月更文挑战第33天】在数字化时代的浪潮中,后端服务的效率和可靠性成为企业竞争的关键。本文将深入探讨如何利用Node.js和Express框架构建高效且易于维护的后端服务。通过实践案例和代码示例,我们将揭示这一组合如何简化开发流程、优化性能,并提升用户体验。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
1月前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的融合之道
【10月更文挑战第31天】在追求快速、灵活和高效的后端开发领域,Node.js与Express框架的结合如同咖啡遇见了奶油——完美融合。本文将带你探索这一组合如何让后端服务搭建变得既轻松又充满乐趣,同时确保你的应用能够以光速运行。
37 0
|
1月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
30 1
JavaScript中的原型 保姆级文章一文搞懂
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
110 2
|
1月前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
21 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
155 4