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

简介: 重学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库来发送网络请求。


相关文章
|
19天前
|
开发框架 JavaScript 安全
js开发:请解释什么是Express框架,以及它在项目中的作用。
【4月更文挑战第24天】Express是Node.js的Web开发框架,简化路由管理,支持HTTP请求处理。它包含中间件系统用于日志、错误处理和静态文件服务,集成多种模板引擎如EJS、Jade、Pug。框架还提供安全中间件提升应用安全,并具有良好的可扩展性,便于项目功能扩展和开发效率提升。
26 3
|
16天前
|
JavaScript 前端开发 持续交付
【专栏】Vue.js和Node.js如何结合构建现代Web应用
【4月更文挑战第27天】本文探讨了Vue.js和Node.js如何结合构建现代Web应用。Vue.js作为轻量级前端框架,以其简洁易懂、组件化开发、双向数据绑定和虚拟DOM等特点受到青睐;而Node.js是高性能后端平台,具备事件驱动、非阻塞I/O、丰富生态系统和跨平台优势。两者结合实现前后端分离,高效通信,并支持热更新、持续集成、跨平台和多端适配,为开发高性能、易维护的Web应用提供强有力的支持。
|
16天前
|
存储 JavaScript 前端开发
❤Nodejs 第十四章(node中间件multer的认识安装使用)
【4月更文挑战第14天】Multer是Node.js用于处理multipart/form-data的中间件,专注于文件上传。。基本用法包括设置存储引擎,如磁盘存储(DiskStorage)或内存存储(MemoryStorage),并指定处理单个或多个文件的方法。例如,`multer.single(&#39;file&#39;)`处理单个文件上传。存储引擎DiskStorage适合永久保存,而MemoryStorage适合临时处理。可以通过`limits`选项限制文件大小,实现不同类型的文件有不同的大小限制。
29 0
|
13天前
|
JavaScript API 开发者
深入了解Node.js的文件系统:Node.js文件系统API的使用与探索
【4月更文挑战第30天】本文深入探讨了Node.js的文件系统API,介绍了如何引入`fs`模块进行文件操作。内容包括异步读取和写入文件、删除文件、创建目录以及使用文件流进行高效操作。此外,还提到了文件系统的监视功能,帮助开发者全面掌握在Node.js中处理文件和目录的方法。
|
13天前
|
开发框架 JavaScript 中间件
深入探索Node.js的Express框架:使用与中间件详解
【4月更文挑战第30天】本文深入探讨了Node.js的Express框架,介绍了其作为Web开发的强大工具,主要聚焦于基本使用和中间件。Express是基于Node.js的Web应用框架,用于构建高效的应用和API。文章详细讲解了如何安装Express,创建简单应用,以及中间件的工作原理和应用,包括中间件的顺序、错误处理和挂载位置。此外,还提到了使用第三方中间件扩展功能。理解Express基础和中间件对于开发高质量Web应用至关重要。
|
15天前
初识express框架
初识express框架
|
19天前
|
Web App开发 JavaScript 前端开发
js开发:请解释什么是Node.js,以及它的应用场景。
Node.js是基于V8引擎的JavaScript运行时,用于服务器端编程。以其事件驱动、非阻塞I/O模型著称,适用于高并发和实时应用。常见用途包括:构建Web服务器、实时应用(如聊天)、API服务、微服务、工具和命令行应用,以及搭配Electron开发桌面软件。
20 1
|
28天前
|
JavaScript 中间件 API
node.js之express的基础知识
node.js之express的基础知识
|
28天前
|
JavaScript 关系型数据库 MySQL
❤Nodejs 第二章(Node连接本地数据库)
【4月更文挑战第2天】本文介绍了如何使用Node.js连接本地MySQL数据库。首先,提到了在MySQL官网下载安装数据库和使用Navicat for MySQL进行数据库管理。接着,通过`yarn add mysql`在项目中安装数据库依赖。然后,创建`app.js`文件,设置数据库连接参数,并建立连接进行查询操作。遇到导入模块的错误后,修改导入方式为CommonJS语法。
41 1
|
1月前
|
开发框架 JavaScript 前端开发
【Node系列】Express 框架
Express.js 是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架,提供一系列强大的特性来帮助你创建各种 web 和移动设备应用。
36 2