【2023秋第7节课】认识 node.js、包管理工具

511 阅读17分钟

QQ图片20231028231625.png

本文编写自26届同学:柏木英理°

Node.js是什么?

Node.js是一个开源的,跨平台的Javascript运行环境runtime

通俗来说,可以理解为它就是一款应用程序,可以用来运行Javascript

注意:Node.js中不能使用BOM和DOM web api

Node.js的下载安装

有两种方式,我们推荐第二种:

1.直接去官网nodejs.org

点击左边这个下载LTS(长期支持版本),下载后,双击安装包根据提示一步一步安装即可,安装时建议不要修改安装路径,直接安装到默认位置。

nodejs官网.png

nodejs安装包安装过程.png 打开安装包一直点next即可,遇到这一选项时可以勾选可以不勾,推荐你勾选

之后在命令行工具中输入node -v可以查看当前node的版本,如果出现以下内容代表安装成功(命令行工具可以在搜索栏中输入cmd打开)

node-v版本显示.png

2.通过nvm安装

除了直接安装外,也可以通过安装工具来安装,使用安装工具安装后更方便我们在不同的node版本之间进行切换,使用起来更加灵活。

这里我们以window下的nvm为例来演示,首先打开github.com/coreybutler…,下载最新版的nvm-setup.exe。根据提示下一步下一步即可,同样推荐安装到默认路径。在命令行中输入nvm version后,出现版本即表示安装成功

nvm安装成功.png

此处仅仅是安装了nvm,并没有安装node,接下来我们还需要通过命令行的形式安装node。

NVM配置镜像服务器

nvm毕竟还是国外的软件,由于一些特殊原因在国内访问时会有无法下载node的情况出现,这时我们只需要将nvm的服务器修改为国内的镜像服务器即可解决该问题。比如,可以通过如下代码将nvm的node和npm镜像服务器修改为国内的阿里云:

nvm npm_mirror https://npm.taobao.org/mirrors/npm/
nvm node_mirror https://npm.taobao.org/mirrors/node/

前面设置的其实是安装 npm 的镜像,而现在是用 npm 去安装 package,所以得设置让 npm 知道去哪获取 package:

npm config set registry https://registry.npm.taobao.org

输入nvm install latest下载并安装最新版的node,输入nvm install lts安装稳定版的node,也可以输入版本号,安装指定版本node。我们还是选择安装LTS版本(长期稳定)

通过nvm安装Node成功.png

之后也可以通过node -v查看到当前node的版本

关于nvm和npm是什么?npm我们后续会详细讲

nvm(Node Version Manager):

nvm 是用于管理 Node.js 版本的工具。它允许你在同一台机器上安装和切换不同版本的 Node.js。这对于不同的项目可能需要不同 Node.js 版本的情况很有用,因为某些项目可能依赖于特定的 Node.js 版本。

npm(Node Package Manager):

npm 是 Node.js 的包管理器,用于管理和分享 JavaScript 代码。它允许开发者下载和安装包(通常是 JavaScript 库和工具),并将这些包添加到项目的依赖中。通过 npm,开发者可以轻松地引入和管理项目中使用的第三方模块,以及分享自己开发的模块供其他人使用。

Node.js初体验

我们可以通过如下几种方式来用node运行js文件(上课演示): 1.在选定的文件夹中打开终端,之后运行node命令

2.在vscode的集成终端中直接运行

Node.js的使用

Buffer(缓冲器)

概念:Buffer是一个类似于数组的对象,用于表示固定长度的字节序列

Buffer本质是一段内存空间,专门用来处理二进制数据

特点:

  1. Buffer大小固定且无法调整
  2. Buffer性能较好,可以直接对内存进行操作
  3. 每个元素的大小为1字节Byte

1.Buffer的创建

  1. alloc

    直接分配内存,并对分配到的这部分内存空间进行清零

  2. allocUnsafe

    直接分配内存,但不对这部分空间做任何操作,即有可能存在着脏数据,故称为Unsafe

  3. from

    从给定的字符串或者数组来进行内存分配和赋值

buffer代码示例.png

运行结果(顺序排列):

buffer中的数据两两一对,以十六进制显示(对应一个字节)

可以发现allocUnsafe在分配一个较大的内存时会出现我们以前存过的数据但没有被清除干净

from则是将字符串中的字母转换成了对应的ascii码(十六进制下的)

2.Buffer的操作

  1. Buffer与字符串的转换

    我们可以借助toString方法将Buffer转换为字符串

    let buf_4 = Buffer.from([105,108,111,118,101,121,111,117])
    console.log(buf_4.toString()) // 输出:iloveyou
    

    toString和from默认是按照utf-8编码方式进行转换的,包含了ascii

  2. Buffer的读写

Buffer可以直接通过[ ]的方式对数据进行处理。

let buf_3 = Buffer.from([105,108,111,118,101,121,111,117])
//读取
console.log(buf_3[1])//读取该buf变量的第二位元素 108
//修改
buf_3[1] = 97 //Buffer被修改为[105,97,111,118,101,121,111,117]
//查看字符串结果
console.log(buf_3.toString()) //输出变为iaoveyou//特殊(这个就不演示了)//toString
let buf = Buffer.from('hello')
console.log(buf[0].toString(2))//此处的toString不是之前的字符串转换了,在加了2这个参数之后将会以二进制的方式输出buf[0]的数据,输出结果为01101000//溢出,即元素超出一个字节能表达的数字(255)
let buf_over = Buffer.from('hello')
buf_over[0] = 361;//舍弃高位的数字 0001 0110 1001 => 0110 1001 超出的部分舍弃
console.log(buf) // 输出:69     即为0110 1001//中文
let buf_ch = Buffer.from('你好')
console.log(buf) // <Buffer e4 bd a0 e5 a5 bd>
// 因为中文在utf-8的编码中占有3个字节,所以两个中文字占六个元素

fs模块(文件系统)

fs模块可以实现与存储的交互,例如文件的创建,删除,重命名,移动以及读写等操作,Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本。(当然异步更加重要)

一、文件写入

文件写入,即创建文件并往里面写入或追加内容的过程

  1. writeFile 异步写入

    语法: fs.writeFile(file,data[,options],callback)

    参数说明:file 文件名 data 待写入的数据 options 选项设置(可选) callback 写入回调

    代码示例:

    // 1.导入fs模块
    const fs = require('fs')
    ​
    //2.写入文件
    fs.writeFile('./zuoyouming.txt','三人行必有我师焉',err => {
        //如果写入失败 err就是一个错误对象,如果成功就是一个null
        if(err){
            console.log("写入失败!")
            return;
        }
        console.log('写入成功!')
    })
    //输出结果:写入成功!

writeFile写入成功.png > 可以看到终端中输出写入成功,并且左侧文件栏出现了zuoyouming.txt

  1. writeFileSync 同步写入

    和writeFile写法大致相同,不需要写callback回调函数

  2. appendFile/appendFileSync 追加写入

    appendFile与writeFile写法完全相同

    writeFile也可以实现追加写入

  3. createWriteStream 流式写入

    语法:fs.createWriteStream(path[,options])

    参数说明:path 文件路径 options 选项配置(可选)

    返回值:object

    代码示例:

    // 1.导入fs模块
    const fs = require('fs')
    ​
    //2.创建写入流对象  相当于与我们要操作的文件建立一个通道,当我们想做操作时就借助这个通道
    const ws = fs.createWriteStream("./term.txt")
    ​
    //3.write
    ws.write("半亩方塘一鉴开\r\n")
    ws.write("天光云影共徘徊\r\n")
    ​
    //4.关闭通道
    ws.end()
    ​
    //结果,创建了一个term文件(如果之前没有),并且写入了相应的内容
    

    应用:

    程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。

    故流式写入方式适用于大文件写入或者频繁写入的场景,writeFile适用于频率较低的场景

  4. 写入文件的场景

    文件写入在计算机中是非常常见的操作,以下场景都用到了文件写入:

    • 下载文件,安装软件
    • 保存程序日志
    • 编辑器(vscode)保存文件
    • 我的讲课视频录制

    需要持久化地保存数据时,我们就应该想到文件写入

二、文件读取

故名思义,就是通过程序从文件中取出其中的数据,有如下几种方式:

  1. readFile 异步读取

    语法:fs.readFile(path[, options], callback)

    参数说明:path 文件路径 options 选项配置 callback 回调函数

    返回值:undefined

    代码示例:

    //1.引入fs模块
    const fs = require('fs')
    ​
    //2.异步读取   data为读取到的数据
    fs.readFile("./term.txt",(err, data) => {
        if(err) {
            console.log('读取失败!')
            return
        }
        console.log(data.toString())
        // 你猜这里为什么有toString?buffer是不是被用上啦?
    })
    //输出:
    //半亩方塘一鉴开
    //天光云影共徘徊//3.同步读取
    let data = fs.readFileSync("./term.txt")
    console.log(data.toString())
    
  2. readFileSync 同步读取

    见上面的代码示例,和写入的类比一下

  3. createReadStream 流式读取

    代码示例:

    //1.引入fs模块
    const fs = require('fs')
    ​
    //2.创建读取流对象
    const rs = fs.createReadStream('./term.txt')
    ​
    //3.绑定data事件  chunk 块儿,一块
    rs.on('data', chunk => {
        //每读出来一块内容就执行一次
        console.log(chunk)
        //结果为Buffer对象  每个Buffer最长为65536字节 即64KB
    })
    ​
    //4.end 可选事件
    rs.on('end', () => {
        console.log("读取完成!")
    })
    
  4. 文件读取的场景

    • 电脑开机,程序运行
    • 通过vscode打开并查看你自己写好的程序
    • 查看图片,播放音乐,播放视频
    • 上传文件

三、文件的重命名与移动

在Node.js中,我们可以使用rename或者renameSync来移动或重命名文件和文件夹

语法:

fs.rename(oldPath, newPath, callback)

fs.reSync(oldPath, newPath)

参数说明:

  • oldPath 文件当前的路径
  • newPath 文件新的路径
  • callback 操作后的回调函数

代码示例:

//1.引入fs模块
const fs = require('fs')

//2.调用rename方法 重命名  newPath为新的名字
fs.rename('./term.txt', './guanshuyougan.txt', err => {
    if(err) {
        console.log('操作失败!')
        return
    }
    console.log("操作成功!")
})

//3.文件的移动 同样是调用rename,如果你将newPath改到不同的地方那就会被移动
fs.rename('./term.txt', '../write/guanshu.txt', err => {
    if(err) {
        console.log('操作失败!')
        console.log(err)
        return
    }
    console.log("操作成功!")
})

//要当心!当你运行完第二块代码的时候文件名字已经被修改了,因此第三块代码的第一个参数可能需要改变

四、文件的删除

两种方式:1.unlink方法 2.rm方法

语法相同 fs.unlink(path, callback) path为要删除的文件, callback为删除后的回调函数

代码示例:

//1.引入fs模块
const fs = require('fs')

//2.调用unlink方法  同步方法unlinkSync
fs.unlink('./term.txt',err => {
    if(err) {
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

//调用 rm 方法 node 14.4版本新增  rmSync
fs.rm('./term.txt',err =>{
    if(err) {
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

五、文件夹的操作

文件夹操作.png

  1. mkdir 创建文件夹

    在node.js中我们可以使用mkdir或者mkdirSync来创建文件夹

    语法:

    fs.mkdir(path[, options], callback)

    fs.mkdir(path[, options])

    参数说明:不说了,和上面一样

    示例代码:

    //1.引入fs模块
    const fs = require('fs')
    
    //2. 创建文件夹 mk make 制作 dir directory 文件夹
    fs.mkdir('./front', err => {
        if(err){
            console.log("创建失败!")
            return
        }
        console.log("创建成功!")
    })
    
    //2.2 递归创建  如果我们想要创建更深层的文件夹
    fs.mkdir('./front/end/course', {recursive: true}, err => {
        if(err){
            console.log("创建失败!")
            return
        }
        console.log("创建成功!")
    })
    
  2. 读取文件夹

    可以读取到对应文件夹中的内容列表(包括当前文件夹)

    语法:fs.readdir(path[, options], callback)

    示例:

    //1.引入fs模块
    const fs = require('fs')
    
    
    //2.readdir读取文件夹
    
    fs.readdir('../read', (err,data) => {
        if(err){
            console.log("读取失败!")
            return
        }
        console.log("读取成功!", data)
    })
    
  3. 删除文件夹

    语法:fs.rmdir(path[, options], callback)

    示例:

    //1.引入fs模块
    const fs = require('fs')
    
    //rm remove 移除
    fs.rmdir('./front', err => {
        if(err){
            console.log("删除失败!")
            return
        }
        console.log("删除成功!", data)
    })
    
    //递归删除 希望删除一个有多层内容的文件夹
    fs.rmdir('./front', {recursive: true}, err => {
        if(err){
            console.log("删除失败!")
            return
        }
        console.log("删除成功!", data)
    })
    

六、查看资源状态

通过fs.stat方法可以查看资源的各种状态:

//1.引入fs模块
const fs = require('fs')

//2. stat 方法 status缩写
fs.stat('../read/term.txt', (err, data) => {
    if(err){
        console.log("操作失败!")
        return
    }
    //输出资源状态
    console.log("操作成功!", data)
    //查看资源是否为文件或文件夹
    console.log(data.isFile())
    console.log(data.isDirectory())
})

path模块

path模块.png

关于绝对路径和相对路径:

绝对路径就是从盘符,从根目录开始到你所在文件夹的目录位置,就好像地图中确切的某个多少经度多少维度的城市,它是由坐标确定了。在计算机中则是从盘符出发直到所在位置的一条路径,这样的路径是唯一的并且大家都有一个共同的起点(统一性)。

相对路径就是描述两个文件之间的相对位置,因此不同的对象之间可以有不同的相对位置。

http模块

在学习这部分内容之前应该先学习http协议和ip等相关知识

我们只做简单的了解就可以,因为有express和koa框架的存在可以让这部分工作更加高效

一个简易的,创建http服务端的例子:

//1.导入http模块
const http = require('http')

//2.创建服务对象   request和response对象可以获取到请求和响应的报文
const server = http.createServer((request, response) => {
    response.end("Hello HTTP Server!") //设置响应体
    //如果需要返回中文内容,则需要先设置响应头
    // response.setHeader('content-type','text/html;charset=utf-8')
})

//3.监听端口  启动服务
server.listen(9000, () => {
    console.log("服务已经启动....")
})
// 有可能遇到9000端口已经被占用的情况,
// 可以换一个端口,也可以用任务管理器找到对应的进程进行关闭
// http协议的默认端口为80,https为443

获取HTTP请求报文

request对象.png

想要获取请求的数据,需要通过request对象

获得请求体中的数据

//1.导入http模块
const http = require('http')

//2.创建服务对象   request和response对象可以获取到请求和响应的报文
const server = http.createServer((request, response) => {
    //1.声明一个变量
    let body = ''
    //2.绑定 data 事件
    request.on('data', chunk => {
        body += chunk
    })
    request.on('end', () => {
        console.log(body)
        response.end("Hello HTTP Server!") //设置响应体
    })
})

//3.监听端口  启动服务
server.listen(9000, () => {
    console.log("服务已经启动....")
})

注意,直接在浏览器里面输入url会发送get请求,get请求没有请求体故body的输出会为空,我们会以一个form表单去发送post请求

如果需要获取url中的路径和查询参数,则需要引入url模块

//1.导入http模块和url模块
const http = require('http')
const url = require('url')

//2.创建服务对象
const server = http.createServer((request, response) => {
    //1.可以直接从resquest对象中获取到url
    console.log(request.url)
    //2.解析 request.url
    let res = url.parse(request.url)
    console.log(res)//输出结果为一个对象
    
    //可以通过res.pathname和query获取具体的路径和查询字符串

    response.end("Hello HTTP Server!") //设置响应体  
})

//3.监听端口  启动服务
server.listen(9000, () => {
    console.log("服务已经启动....")
})
//也可以使用new URL来进行操作,可以自行了解

HTTP设置响应

我们可以通过对response对象进行操作来写入响应

代码示例:

//1.导入http模块
const http = require('http')

//2.创建服务对象
const server = http.createServer((request, response) => {
    //1.设置响应状态码
    response.statusCode = 404

    //2.设置响应状态的描述(基本用不上)
    // response.statusMessage = 'xxx'
    //3.响应头的设置,之前已经用过啦!
    // response.setHeader('content-type','text/html;charset=utf-8')
    // response.setHeader('myHeader','big')
    // 总之就是键值对的形式


    //响应体的设置  如果我们用write了一般就不用end写响应体了
    response.write('love')//write可以用多次
    response.write('love')
    response.end('response') // end只能写一次并且必须写一次

})

//3.监听端口  启动服务
server.listen(9000, () => {
    console.log("服务已经启动....")
})

Node.js模块化

一、什么是模块化与模块?

将一个复杂的程序文件依据一定的规范拆分成多个文件的过程称之为模块化(此处的规范在node中是commonJS规范,两者的关系类似于Javascript与ECMAScript)

其中每一个拆分的文件都是一个模块,模块的内部数据是私有的,不过模块可以选择暴露其内部数据以便于其他模块使用实现模块间的通信。

模块化的好处:

  1. 防止命名冲突
  2. 高复用性
  3. 高维护性

二、模块化的具体操作(暴露,导入模块)

导入数据注意事项.png

引入其实我们已经使用过用require引入http模块

关于引入文件夹情况,可以先看完npm包管理工具的知识点在来看

代码示例:

//第一个文件 b.js
function miao () {
    console.log("喵喵")
}
function wang () {
    console.log("汪汪")
}

// exports = module.exports = {} 本身是一个空对象
// 所以不能使用exports = value的方式暴露数据
exports.miao = miao
exports.wang = wang

//或者module.exports 如果只暴露一个的话可以单行去掉花括号
// module可以暴露任意数据,可以在这里定义好数据直接暴露出去
// module.exports = {
//     miao,
//     wang
// }
// module.exports = 'i love u'

//第二个文件 a.js
//将b模块导入
const b = require('./b.js')

//输出b暴露的模块
console.log(b)

//调用暴露的方法
b.miao()

npm包管理工具

关于包?:包的单词是package,代表了一组特定功能的源码集合

关于npm?:

npm全称Node Package Manager,译为Node的包管理工具,故npm为node.js官方的包管理工具

npm就是一个管理包的应用软件,可以对包进行下载安装,更新,删除,上传等操作

借助包管理工具可以快速开发项目提高开发效率

包管理工具是一个通用的概念,很多语言都有包管理工具,因此掌握好此知识很重要!

常见的包管理工具有:1. npm 2. yarn 3.cnpm

我们目前了解npm即可,后续可以基于npm的理解去学习其他的工具

npm的基本使用

关于安装:在我们安装node的时候会自行安装好对应版本的npm,可以通过npm -v 查看版本号

初始化

交互式创建包的过程:

npm init.png

从上往下依次为 包的名称 包的版本号 包的介绍说明 包的入口 自定义测试命令 git仓库 密码 作者 开源证书。 我们基本不用管,填个包的名称就行,其他都不填或者默认

之后就会生成一个package.json配置文件(记得吗?我们之前说过模块化引入文件夹的情况)

packagejson和注意事项.png

搜索并下载我们需要的包

我们可以到 www.npmjs.com/官方网站中搜索我们需要的包

下载包.png

示例:

通过npm i 进行下载,然后直接导入包

uniq包的使用示例代码.png

npm安装uniq包并运行.png

输出结果见上图

选择性学习:require导入npm包时发生了什么?

require引入npm包.png

生产依赖与开发依赖

生产与开发环境.png

由于有些包只在我们开发的时候需要这个包,正式上线项目后客户并不需要,就可以被删除掉,因此我们需要区分生产环境和开发环境。

做饭的人需要铲子,吃饭的人不需要。

具体什么时候用,用哪个,看你做的项目而言

包的全局安装

npm全局安装.png

如果安装完全局的包之后,无法通过终端运行全局的命令可以修改windows的执行策略

见链接视频第P82

www.bilibili.com/video/BV1gM…

安装包的所有依赖(因为git不会保存node_modules的内容)

安装包的所有依赖.png

通过这种方式可以根据项目中已经有的package.json快速安装你开发的项目所需要的包

npm安装指定版本的包,发布与更新包,删除包的操作同学们自行学习

作业

一、用fs模块实现一个文件复制功能程序( 流式 )

二、学习Node的事件循环机制,宏任务与微任务相关知识

参考:juejin.cn/post/684490…

三、学习npm安装指定版本的包,发布与更新包,删除包的操作

学有余力可以学习express和koa框架