前言
简单来说NodeJS是一个开源的、跨平台的JavaScript的运行环境,它可以进行服务端应用(比如视频发表在bi站上被别人评论)、工具类的应用(webpack、Vite、babel)以及桌面端应用(vscode、figma、postman)的开发
安装
说明: 去NodeJS官网(这里给出的是NodeJS在国内的镜像网站,因为官网的进入需要加速器才可以)在官网中,node会存在两种下载的方式(LTS版本:长期支持的版本,推荐大多数用户去下载)(current版本:当前版本, 当前最新的补丁,但是不稳定),在下载好安装包之后,一般情况下一直下一步就可以安装好了(安装好之后在命令行使用node -v可以查看版本号来确定是否已经安装)
常用命令
// 退出 ctrl + c // 自动补全路径 Tab // 进入该文件夹 cd 文件夹名称 // 返回到上一级文件夹 cd.. 文件夹名称 // 用于切换已经使用过的命令 上下方向键 // 查看当前文件夹里面的内容 dir // 清屏 cls
NodeJS的全局对象global
说明: 在NodeJS中是不存在window对象的,当然也不存在dom和bom的操作,不过这个对象由global(console方法和定时器是可以使用的)来代替,不过在浏览器中,是不支持global这个对象的,在浏览器中由globalThis(等价于window)对象来代替,这两个对象是等价的。
Buffer
概念: 它是一个类似于数组的对象,用于处理二进制数据,它是NodeJS内置的一个模块并且无需导入就可以使用(类似全局变量)
特点: 它的大小是固定的(不能够调整),而且性能完好,可以直接对计算机的内存进行操作并且每个元素的大小为一个字节
创建:
// alloc:用这种方式创建的Buffer的每一个二进制位都会归零(每次创建都是独立 // 的数据,不会包含旧的数据) let buf = Buffer.alloc(20) // allocUnsafe:用这种方式创建的Buffer是不安全的,它可能会包含旧的内存数据, // 但是创建的速度比上面的一种要快 let buf = Buffer.allocUnsafe(20) // from:在转换的时候每个字符都会与ascll码表中对应的数字(用16进制表示) let buf = Buffer.from(20) // from:也能够将数组内的每个元素转换为二进制进行存储 let buf = Buffer.from([10, 20, 30])
Buffer与字符串的转换:
// 转换的话是用过toString的方法(utf-8编码)来操作的 let buf = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]) console.log( buf.toString() )
Buffer的读取与改写:
// 像数组那样用[]来操作 let buf = Buffer.from('hello') // 这里默认得到10进制的表示方法 console.log( buf[0] ) // 可以用[]的方式直接进行赋值 buf[0] = 100
注意:
// 中文:一个utf-8的中文字符所占的字节数是3个 // 溢出:当储存的数据大于255的时候,它会先将数据转换为二进制的表示形式, // 然后从左到右数出8位数,其他的数据会被删除(舍弃高位数字) buf[0] = 361 // 0001 0110 1001 (361) 舍弃--> 0110 1001 (105)
fs模块(文件系统):
作用: 可以实现与硬盘的交互以及文件夹的相关操作
文件的写入:
// 异步写入 // 参数:文件路径,文件内容,配置选项(可选),写入的回调函数 // 1.导入fs模块 const fs = require('fs') // 2.写入文件(文件不存在会自动创建) fs.writeFile('1.txt', 'hahahha', { // 写完这个就能够起到文件内容追加的效果了 flag: 'a' }, err => { // 如果写入失败err表示一个错误的对象,写入成功就是null if(err){ console.log('写入失败') return false } console.log('写入成功') })
// 同步写入:会阻塞进程 // 参数:文件路径,文件内容,配置选项(可选) // 1.导入fs模块 const fs = require('fs') // 2.写入文件(文件不存在会自动创建) fs.writeFileSync('1.txt', 'hahahha')
文件的追加:
// 异步追加 // 参数:文件路径,文件内容,配置选项(可选),追加操作后执行的回调函数 // 1.导入fs模块 const fs = require('fs') // 2.追加内容(文件不存在会自动创建) fs.appendFile('1.txt', 'hahahha', err => { // 如果追加失败err表示一个错误的对象,追加成功就是null if(err){ console.log('追加失败') return false } console.log('追加成功') })
// 同步追加:会阻塞进程 // 参数:文件路径,文件内容,配置选项(可选) // 1.导入fs模块 const fs = require('fs') // 2.追加内容(文件不存在会自动创建) // \r\n: 表示换行的意思 fs.appendFileSync('1.txt', '\r\nhahahha')
文件的流式写入:
// 1.导入fs模块 const fs = require('fs') // 创建写入流对象(参数为文件路径,没有会创建) const ws = fs.createWriteStream('./观书有感.txt') // 使用write的方法去写入内容 ws.write('半亩方塘一鉴开') ws.write('天光云影共徘徊') ws.write('问渠那得清如许') ws.write('为有源头活水来') // 关闭写入的通道(可选的) ws.close()
注意:程序打开一个文件是需要消耗资源的,流式写入可以减少打开和关闭文件的次数,所以他适用于大文件的写入或者是频繁写入的场景,而writeFile写入只适合写入频率低的场景,当需要持久化的保存数据的时候,应该想到使用文件写入。
文件的读取:
// 异步读取 // 参数:文件路径,配置选项(可选),读取的回调函数 // 1.导入fs模块 const fs = require('fs') // 2.读取文件 fs.readFile('1.txt', ( err, data ) => { // 如果读取失败err表示一个错误的对象,读取成功就是null // 如果读取失败data表示null,读取成功就是读取到的数据 if(err){ console.log('读取失败') return false } // 这里读取到的数据是一个Buffer的格式 console.log( data.toString() ) })
// 同步读取:会阻塞进程 // 参数:文件路径,配置选项(可选) // 1.导入fs模块 const fs = require('fs') // 2.读取文件(读取成功会将内容返回给变量进行储存起来) let data = fs.readFileSync( '1.txt' ) // 这里读取到的数据是一个Buffer的格式 console.log( data.toString() )
文件的流式读取:
// 1.导入fs模块 const fs = require('fs') // 创建读取流对象(参数为文件路径) const rs = fs.createReadStream('./观书有感.txt') // 绑定data事件 rs.on('data', chunk => { // 每当读取到一块数据的时候,就会执行一次这个函数,chunk就是读取 // 到的数据,并且每次读取数据的内容大小最多为64KB console.log( chunk ) }) // 读取完毕的end事件(可选的) rs.on('end', () => { console.log( '读取成功' ) })
文件的重命名与移动: 重命名与文件的移动都是对文件的路径进行修改(同步与异步操作在于是否有后面的回调函数,其他是一样的)
// 导入fs模块 const fs = require('fs') // 异步文件的重命名 // 参数:旧文件路径,新文件路径,操作后的回调函数 fs.rename('1.txt', '2.txt', err => { // 如果重命名失败err表示一个错误的对象,重命名成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') }) // 异步文件的移动 // 参数:旧文件路径,新文件路径,操作后的回调函数 fs.rename('./data1/1.txt', '../data2/2.txt', err => { // 如果移动失败err表示一个错误的对象,移动成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
文件的删除:
// 导入fs模块 const fs = require('fs') // 文件的删除(异步) --> 同步方法(unlinkSync) // 参数:需要删除文件的路径,删除后的回调函数 fs.unlink('1.txt', err => { // 如果删除失败err表示一个错误的对象,删除成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') }) // 文件的删除(异步) --> 同步方法(rmSync) <=> node = 14.4新增 // 参数:需要删除文件的路径,删除后的回调函数 fs.rm('1.txt', err => { // 如果删除失败err表示一个错误的对象,删除成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
文件夹相关操作:
// 单个文件夹的创建: const fs = require('fs') // 参数:创建的文件路径,配置选项(可选),创建后的回调函数 fs.mkdir('./a', err => { // 如果创建失败err表示一个错误的对象,创建成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
// 文件夹的嵌套创建: const fs = require('fs') // 参数:创建的文件路径,配置选项(可选),创建后的回调函数 fs.mkdir('./a/b/c', { // 这个选项表示可以去嵌套的去创建文件夹 recursive: true } err => { // 如果创建失败err表示一个错误的对象,创建成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
// 文件夹的读取: const fs = require('fs') // 参数:读取的文件路径,配置选项(可选),读取后的回调函数 fs.readdir('./a', ( err, data ) => { // 如果读取失败err表示一个错误的对象,读取成功就是null // 如果读取失败data表示null,读取成功就是读取到的数据 if(err){ console.log('操作失败') return false } console.log( data ) })
// 单个文件夹的删除: const fs = require('fs') // 参数:删除的文件路径,配置选项(可选),删除后的回调函数 fs.rmdir('./a', err => { // 如果删除失败err表示一个错误的对象,删除成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
// 文件夹的嵌套删除: const fs = require('fs') // 参数:创建的文件路径,配置选项(可选),创建后的回调函数 fs.rm('./a/b/c', { // 这个选项表示可以去嵌套的去删除文件夹 recursive: true } err => { // 如果删除失败err表示一个错误的对象,删除成功就是null if(err){ console.log('操作失败') return false } console.log('操作成功') })
查看文件的状态:
// 文件夹的读取: const fs = require('fs') // 参数:查看的文件路径,配置选项(可选),查看后的回调函数 fs.stat('./a', ( err, data ) => { // 如果查看失败err表示一个错误的对象,查看成功就是null // 如果查看失败data表示null,查看成功就是读取到的数据 if(err){ console.log('操作失败') return false } console.log( data ) // data.isFile():可以查看是否是一个文件(返回布尔值) // data.isDirectory():可以查看是否是一个文件夹(返回布尔值) })
__dirname: 这是一个NodeJS中的一个全局变量,它保存的是当前文件所在目录的绝对路径(物理路径),而相对路径的参照物是命令行的工作目录,路径的切换可能就达不到你想要的要求,这也是使用相对路径存在的问题,此时就可以使用__dirname来将相对路径替换成绝对的路径,来解决这些问题。
__filename: 这个保存的是当前文件所在的绝对路径。
path模块(路径模块)
常用方法:
// path.resolve(__dirname, 相对路径):拼接规范的绝对路径 // 注意:这个方法的第一个参数一般用__dirname,后面的参数都跟相对路径, // 如果后面参数绝对路径的话,那么就会以后面的绝对路径作为开头再 // 往后拼接(这个方法是很常用的)。 // path.sep:获取操作系统的分隔符 // path.parse:解析路径并返回对象 // path.basename:获取路径的基础名称 // path.dirname:获取路径的目录名称 // path.extname:获取路径的扩展名
绝对路径常见形式:
// 完整写法(会直接向目标资源发送请求,一般网站的外部链接会用的多) https://juejin.cn/editor // 缺少域名(会先与URL的协议进行拼接再去发送请求,一般大型网站会用) //juejin.cn/editor // 只保留路径(会先与URL的协议、域名、端口进行拼接再去发送请求,中小型网站会用) /editor
HTTP协议:
我的理解: HTTP协议是一种浏览器与服务器之间的通信进行的约束,
浏览器发出请求向服务器获取数据,服务器收到请求后会向浏览器响应数据,其中请求的内容称为请求报文,响应的数据称为响应报文,对于报文而言,它又三部分组成,分别是请求行(报文的第一行)、请求头(报文的第二行开始一直到空行前结束)、请求体(空行之后的内容)(这个部分内容格式没有规定,与后端开发规定为准)
请求行: 请求行由三部分组成,分别是请求方法、请求地址、HTTP的版本号
// 请求方法(常见以下几种): GET:主要用来获取数据,参数存在于地址栏中,所以不怎么安全,一般大小为2k POST:主要用来新增数据,参数存在与请求体内,相对安全,没有大小的限制 PUT/PATCH:主要用于更新数据 DELETE:主要用于删除数据 // 请求地址(URL): 主要用来定位资源所在的位置的 // 组成部分:协议 :// 域名(主机名) 端口号 路径 参数 // 其中协议和域名是必要的,其他都是可选的 // HTTP版本号对应的发布时间: 1.0 --> 1996年 1.1 --> 1999年 2 --> 2015年 3 --> 2018年
请求头: 它存储了浏览器的相关的信息,格式的话类似于间值对的形式存储,如果想要知道每个请求头的具体作用,可以去MDN的官网查看
响应报文: 同样由请求行、请求头和请求体三部分组成,对于请求行来说,它由HTTP版本号、响应状态码和响应状态的描述三部分组成,这些部分可以去MDN的官网查看,文档中写的很详细。
IP地址
说明: IP地址本身是一个数字的标识,它由32位二进制数组成,然后每8位一组,将每一组转换为二进制的数字,中间使用.号进行连接之后,得到的就是一个IP地址了,可以将它理解为网络中的身份证,每个IP是唯一的。
IP类型:
// 本机回环IP(访问这个IP地址等于访问自己) 127.0.0.1 - 127.255.255.254 // 局域网IP(作用范围较小) 192.168.0.0 - 192.168.255.255 172.16.0.0 - 172.31.255.255 10.0.0.0 - 10.255.255.255 // 广域网IP(除上述IP地址以外)(作用范围非常大)
端口: 它是应用程序的数字标识,一台现代计算机有65536个端口(数字标识从0 - 65535),一个程序可以使用一个或者多个端口,端口的作用在于可以实现不同主机应用程序之间的通信
HTTP模块
创建:
// 1.导入HTTP的模块 const http = require('http') // 2.创建服务对象 // createServer这个函数的参数是一个函数,这个函数有两个参数, // 第一个是对请求报文的封装(一般用request表示),第二个是对 // 响应报文的封装(一般用response表示),这个函数在接收到http // 的请求后就会执行 const server = http.createServer((request, response) => {}) // 3.监听端口,启动服务 // listen这个方法第一个参数为监听的端口,第二个为服务启动后执行的函数 //(包括其中的操作) server.listen(9000, () => {})
注意:
1.命令行 Ctrl + c 可以停止服务
2.更新代码需要重新启动服务
3.端口号被占用的时候可以选择停止对该端口的监听或者切换端口
4.HTTP协议的默认端口号是80,常见的端口号有3000、8080、8090等
5.response.setHeader('content-type', 'text/html;charset=utf-8')可以解决中文乱码问题或者使用meta标签进行utf-8的格式设置
请求头和请求行的获取:
// 1.导入HTTP的模块 const http = require('http') const server = http.createServer((request, response) => { // 获取请求的方法 request.method // 获取请求的URL(只包含url中的路径与查询字符串) request.url // 获取HTTP协议的版本号 request.httpVersion // 获取HTTP的请求头 request.headers })
请求体的获取:
// 1.导入HTTP的模块 const http = require('http') // 2.创建服务对象 const server = http.createServer((request, response) => { // 1.声明一个变量,用来接收响应体的结果 let body = '' // 2.绑定data事件 // request是一个可读流的对象,可以用文件的流式读取的方式获取数据 request.on('data', chunk => { body += chunk }) // 绑定end事件 // 当数据完全取出的时候所做的一些操作 request.on('end', () => { console.log(body) }) })
响应报文的设置:
// 1.导入HTTP的模块 const http = require('http') // 2.创建服务对象 const server = http.createServer((request, response) => { // 设置状态码 response.statusCode = 200 // 设置响应状态的描述 response.statusMessage = '请求成功' // 设置响应头 response.setHeader('test', '设置单个的响应头') response.setHeader('test', ['a', 'b', 'c', '设置多个属性名相同的相应头,用数组表示' ]) // 设置响应体 response.write('write的响应体可以设置多次,但是end的响应体有且只能设置一次, 每个函数的末尾必须存在一个end的响应体的设置') })
MIME类型: 它也叫做媒体类型,是一种标准,用来表示文档、文件或子节流的性质和格式,HTTP服务可以通过设置响应头来表明响应体的MIME类型,浏览器会根据类型来决定如何处理资源(常见的MIME类型如下)
// 格式:[主类型]/[副类型] // 常见的MIME类型: html --> 'text/html' css --> 'text/css' js --> 'text/javascript' png --> 'image/png' jpg --> 'image/jpeg' gif --> 'image/gif' mp4 --> 'video/mp4' mp3 --> 'autio/mpeg' json --> 'application/json'
对于未知的资源类型的话,可以选择设置application/octet-stream类型,浏览器在遇到该类型的响应的时候,会对响应体内容进行独立存储,也就是常见的下载的功能
URL模块
请求路径信息的获取:
// 1.导入HTTP的模块 const http = require('http') // 导入URL的模块 const url = require('url') // 2.创建服务对象 const server = http.createServer((request, response) => { // 解析路径 // 会将路径解析成一个对象,相比request.url来说可以更清楚参数路径什么的 // 参数:解析的字符串,布尔值(如果为true,那么解析的对象的query属性将 // 会变成一个参数) url.parse(request.url) })
注意:
1.request.url只能获取路径以及参数,无法获取URL中的域名以及协议的内容
2.request.header将请求信息转换为一个对象,并将所有的属性名都小写表示
3.如果访问网站的时候只填写IP地址或者域名信息,此时请求路径为 '/'
4.favicon.ico属于浏览器自动发送的请求
5.静态资源:内容长时间不会改变的资源,静态资源文件所在的文件夹称为静态文件夹,文件夹所在的目录就是根目录
6.动态资源:内容会经常更新的资源
模块化开发(commonJS规范)
说明: 可以将一个复杂的程序文件按照一定的规范进行拆分,拆分成多个文件的过程称为模块化,其中拆分出来的每一个文件就是一个模块,模块内部的数据是私有的,不过这个数据可以根据自己的需求进行暴露提供给其他的模块来使用
暴露数据的方式:
// 使用export.module = { 暴露的变量或者函数 }的方式来解决 const app = 1 module.exports = { app }
// 使用exports.属性名 = 属性值的方式 exports.app = 'zhangsan'
exports <=> module.exports = { }
导入数据的方式:
// 导入文件的话使用require关键字 // 此时这个data里面就存放暴露的变量,他是一个对象,可以使用解构赋值取值 const data = require('./文件路径') data.app const { app } = require('./文件路径') app
对于require的使用:
1.对于自己写的模块,导入的路径写相对路径,不要省略 ./ 或 ../
2.js和json格式的文件导入时可以不加后缀(最好加上,可以提升性能)
3.导入其他类型的文件的时候会按js文件进行处理
4.如果导入的路径是个文件夹,首先会检测该文件夹下的package.json文件中main属性对应的文件,如果main属性不存在或者package.json文件不存在,会检测这个文件夹下面的index.js和index.json文件,如果还是不存在就会报错
5.导入内置模块的时候直接写模块名就可以(它会在node_modules文件夹中查找)
require导入自定义模块:
// 引入模块 const path = require('path') const fs = require('fs') function require(file) { // 将相对路径转换为绝对路径,从而定位目标文件 let absolutePath = path.resolve(__dirname, file) // 缓存检测 if(caches[absolutePath]){ return caches[absolutePath] } // 读取文件的代码 let code = fs.readFileSync(absolutePath).toString() let module = {} let exports = module.exports = {} // 包装成一个函数再执行 (function(exports, require, module, __dirname, __filename){ // 这里展示对外暴露的变量和函数 const test = { name: '学习使我快乐' } // 这里对外暴露变量和函数 module.exports = test // 输出在终端 console.log(arguments.callee.toString()) })(exports, require, module, __dirname, __filename) // 缓存结果 caches[absolutePath] = module.exports // 返回module.exports的值 return module.exports }
包管理工具
说明: 它有点类似于下载应用市场,它可以进行包的下载、更新、删除等操作(这里的包跟vscode中的插件类似),然后通过下载的一些包来简化开发写代码的一些操作(一般每个下载的包都会有响应的文档,去官网查看文档去使用下载的包)来提升自己的开发效率
npm: npm是node.js官方内置的包管理工具,这个工具在node.js安装成功之后就会自动安装npm,可以通过npm -v来检测是否已经安装成功
// 初始化一个package.json文件用来管理下载的包: // 使用命令 npm init -y // 得到以下内容 // 默认生成的package.json文件 { // 包的名字(不能使用中文、大写,默认使用文件夹的名字,所以文件夹名同样 不可以使用中文和大写的字母) "name": "86139", // 包的版本号(格式为x.x.x,其中x表示数字,默认为1.0.0) "version": "1.0.0", // 包的入口文件 "main": "index.js", // 命令别名的设置(每一行代表一个命令) "scripts": { // 假如在命令行需要执行node 02.js这个命令的时候,这里就可以使用 // npm run end代替(但是运行start命令的时候有点特别,它能够不使 // 用run这个单词) "start": "node 01.js" "end": "node 02.js" }, "keywords": [], // 作者 "author": "", // 开源证书(ISC整数和MIT证书功能相同) "license": "ISC", // 包的描述 "description": "" } // 也可以使用命令 npm init 进行手动的初始化上面的内容(问答形式)
// 常用命令: // 查看 npm 版本 npm -v: // 初始化后会出现一个 Package.json 配置文件,可以在后面加上 -y, // 快速跳到问答界面 npm init: // 会根据项目中的 package.json 文件自动给下载项目中所需的全部依赖 //(安装在dependencies中,也就是说生产和开发都会用到) // 这个会安装具体版本号的包(下面的安装同理) npm install 包名@版本号 / npm i 包名: // 安装的包只用于开发环境,不用于生产环境,会出现在 package.json // 文件中的 devDependencies 属性中 npm insall 包名 --sava-dev (npm install 包名 -D) : // 安装的包需要发布到生产环境的,会出现在 package.json 文件中的 // dependenceies 属性中 npm insall 包名 --sava (npm install 包名 -S) : // 查看当前目录下已安装的node包 npm list: // 查看全局已经安装过的node包 npm list -g: // 查看npm帮助命令 npm --help : // 更新指定包 npm update包名 : // 卸载指定包 npm uninstall 包名 : // 查看配置信息 npm config list : // 查看指定命令的帮助 npm 指定命令--help : // 查看远程npm上指定包的所有版本信息 npm info 指定包名 : // 查看当前包的安装路径 npm root : // 查看全局的包的安装路径 npm root -g : // 查看本地安装的指定包及版本信息,没有显示empty npm ls 包名 : // 查看全局安装的指定包及版本信息,没有显示empty npm ls 包名 -g : // 将npm设置为淘宝镜像: npm config set registry https://registry.npm.taobao.org // 切换回默认全局镜像 npm config set registry https://registry.npmjs.org // 查看npm镜像设置: npm config get registry // 查看npm配置 npm config list
生产模式与开发模式:
dependencies(生产):是生产和开发都会用到的依赖包,会被打包到项目中。 devDependencies(开发):是只在开发环境中使用的依赖包,不会被打包到项目中。
cnpm: cnpm是一个npmjs.com的完整镜像,它部署在国内的服务器上面,目的一般就是为了加速下载相关源文件。原理上来说,cnpm做的事情,npm也能做,只是在执行命令时将npm改为cnpm
// 安装国内镜像(变相的安装cnpm) npm install -g cnpm --registry=https://registry.npm.taobao.org // 或 npm config set registry https://registry.npm.taobao.org // 然后在使用命令的时候将npm换成cnpm,命令执行的效果是一样的
yarn: 由Facebook在2016年推出的包管理工具,但它的官网时而可以打开时而打不开,不过它下载速度很快(因为它会缓存下载过的包)并且安全(因为会通过算法检验包的完整性)可靠(因为它拥有检测的文件格式以及安装的算法)
// 常用命令: // 安装yarn npm install -g yarn // 安装成功后,查看版本号 yarn --version / yarn -v // 初始化项目,会生成package.json文件 yarn init //显示所有配置项: yarn config list //显示某配置项: yarn config get <key> //删除某配置项: yarn config delete <key> //设置配置项: yarn config set <key> <value> [-g|--global] // 安装package.json里所有包: yarn install //安装一个包的单一版本: yarn install --flat //强制重新下载所有包: yarn install --force //只安装dependencies里的包: yarn install --production //不读取或生成yarn.lock: yarn install --no-lockfile //不生成yarn.lock: yarn install --pure-lockfile //在当前项目中添加一个依赖包,会自动更新到package.json: yarn add [package] //安装指定版本,这里指的是主要版本,如果需要精确到小版本,使用 -E参数: yarn add [package]@[version] // 发布包: yarn publish // 移除一个包: yarn remove [packageName] // 用于更新包到基本规范范围的最新版本: yarn upgrade // 用来执行在package.json中scripts属性下定义的脚本:(start属性不需要run) yarn run 属性 // 列出已缓存的每个包: yarn cache list // 返回 全局缓存位置: yarn cache dir // 清除缓存: yarn cache clean
nvm: 这个简单来说就是用来方便node版本的切换的,只不过安装之前需要先将本地安装好的node,不然安装会出现问题,安装流程跟node的安装流程类似。
// 常用命令: // 显示node是运行在32位还是64位 nvm arch [32|64] // 显示已安装的node版本的列表 nvm list // 开启node.js版本管理。 nvm on // 关闭node.js版本管理 nvm off // 卸载指定版本node nvm uninstall <version> // 使用制定版本node。可指定32/64位 nvm use [version] [arch] // 显示nvm版本。version可简化为v nvm -v // 安装自己需要的版本 nvm install 版本A // 选择使用该版本 nvm use 版本A // 卸载之前的老版本 nvm uninstall 老版本
express框架
介绍: 它是一个基于Node.js平台开发的一个框架,便于我们开发web应用(可以理解为它是一个封装好的工具包,封装了很多的功能在里面)
基本使用(4步走):
// 1.导入express框架 const express = require('express') // 2.创建应用对象 const app = express() // 3.创建路由 app.get('./home', (request, response) => { // request:请求报文的封装对象 // response:响应报文的封装对象 request.end('hello express') }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('服务已经启动,这个是给程序员看的,只能在终端看见') })
路由: 这个可以理解为根据一定的条件将用户所需要的界面展示给他(比如说用户点击注册按钮表示它想去注册页面,这个跳转的条件是他点击了注册的按钮,同理,如果他点击登陆按钮的话,那么又需要展示登陆的界面,然而这种页面的切换是由路由来操作的)
// 路由的基本使用: // 1.导入express框架 const express = require('express') // 2.创建应用对象 const app = express() // 3.创建路由 // 一个路由需要提供请求的方法(get / post)、请求路径和回调函数 //(当跳转的路径与路由的路径相同时,函数会执行) // 一般路由存在下面五种情况之一: // 路径为'/'的时候它默认表示首页 app.get('/', (request, response) => { request.end('hello about') }) // 请求方式为get,路径为'/home'的路由(地址栏的直接输入是get请求) app.get('./home', (request, response) => { request.end('hello home') }) // 请求方式为post,路径为'/about'的路由(ajax和表单是post请求) app.post('./about', (request, response) => { request.end('hello about') }) // 请求方式为get或者是post都可以,路径为'/all'的路由 app.all('./all', (request, response) => { request.end('hello all') }) // 路径为'*'的时候它匹配所有页面,但由于程序从上到下执行, // 把这个路由写在最后表示与规定的所有页面均不匹配,那么这个路由可以作为404页面 app.all('./all', (request, response) => { request.end('hello all') }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('服务已经启动,这个是给程序员看的') })
路由的模块化:
// 模块化的主要是为了可以方便代码的管理,使代码维护起来要容 // 易一些(如果一个后台程序代码都在一个文件中,那么可想而知 // 需要一个错误是有多困难) // 引入express框架 const express = require('express') // 创建路由对象(类似上面创建的app的对象) const router = express.Router() // 书写路由配置 router.all('./all', (request, response) => { request.end('hello all') }) // 将路由对象暴露出去 module.exports = router // 然后在使用的时候将这个文件导入(用变量存储起来),之后使用use的 // 方法进行注册就可以实现路由模块化的开发了
路由的参数:
// 路由的params参数:需要在路径的后面指明参数的具体位置,跟vue的params参数是 // 一样的 const express = require('express') const app = express() app.all('./all:id', (request, response) => { // 使用 request.params.参数名 就可以拿到具体的参数的值了 request.params.id request.end('hello all') })
请求参数的获取:
// 这里请求参数的获取跟HTTP的有些参数获取的操作是一样的(比如) // 1.导入express框架 const express = require('express') // 2.创建应用对象 const app = express() // 3.创建路由 app.get('./home', (request, response) => { // 获取请求的方法 request.method // 获取请求的URL(只包含url中的路径与查询字符串) request.url // 获取HTTP协议的版本号 request.httpVersion // 获取HTTP的请求头 request.headers // express的一些方式(比如) // 获取请求的路径 request.path // 获取请求的参数 request.query // 获取请求的ip地址 request.ip // 获取请求头 request.get('请求头的属性名') request.end('hello express') })
响应报文的设置: 它完全兼容HTTP响应报文内容的设置,但它也有自己设置的一些方法(如下)
// 例如: // 1.导入express框架 const express = require('express') // 2.创建应用对象 const app = express() // 3.创建路由 app.get('./home', (request, response) => { // 设置响应的状态码 response.status(500) // 响应头的设置 response.set('响应头名','响应头的内容') // 响应体的设置 response.send('内容') // 支持链式写法 response.status(500).set('响应头名','响应头的内容').send('内容') // 跳转响应(重定向URL) response.redirect('URL') // 下载响应(用于下载文件) response.download(文件的绝对路径) // 响应json格式的内容在页面上 response.json({ name: '页面的数据' }) // 响应文件内容在页面上 response.sendFile(文件的绝对路径) }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('服务已经启动,这个是给程序员看的,只能在终端看见') })
中间件: 它是一个函数,它有点类似于js中的预处理函数(就是在执行的时候先通过我这个函数来检查一下,检查一下是否有在我意料之内的错误,并将其检测出来),中间件可以存在多个,执行顺序由上而下,它主要有两种类型全局中间件(就是不管是什么内容都会由这个函数进行检查)以及路由中间件(只有与我的路由规则匹配的时候才会被检测)
// 全局中间件 // 导入express框架 const express = require('express') // 创建应用对象 const app = express() // 全局中间件函数需要放在所有路由的前面,因为它需要最先执行, // 中间件函数参数有三个,分别是请求报文对象,响应报文对象, // next的方法(是否通过检测) function show(request, response, next){ // 这里就可以进行自己想要的预处理操作了,然后判断是否执行 // next的方法,如果执行,就会往下继续执行代码,如果不执行 // 那么代码的执行就会停在此处。 } // 当然,上面只是仅仅定义了中间件的函数,如果没有执行这个函数将没有任何作用 // 不过执行函数需要使用创建好的应用对象的use方法来执行才可以 app.use(show) // 创建路由 app.get('/', (request, response) => { request.end('hello about') })
// 路由中间件:中间件函数的定义还是同样的方法,只不过在使用的时候不再使用use // 去执行了,而是直接将函数名写在路由对象中路径的后面就可以,当执行 // 到这个路由对象的时候,就会执行中间件里面的函数内容,然后根据函数 // 的内容判断是否执行next来进行下一步的操作 // 导入express框架 const express = require('express') // 创建应用对象 const app = express() // 函数也可以使用箭头函数 function show(request, response, next){} // 创建路由 app.get('/', show, (request, response) => { request.end('hello about') })
// 静态资源中间件:这个中间件的作用就是将静态资源部署到服务器上面, // 想要访问静态资源文件夹里面的文件的时候就可以直 // 接在地址栏中通过路径发送请求访问响应的内容 // 导入express框架 const express = require('express') // 创建应用对象 const app = express() // 静态资源中间件的部署 app.use( express.static(静态资源文件夹的绝对路径) )
注意:
1.对于静态资源中间件的部署它默认访问的是其文件夹的index.html,会将其作为首页来渲染(也就是当你在地址栏什么都不用输的时候它默认渲染的页面,那么这个页面是不可以缺少的)
2.如果静态资源文件夹与路由的路径发生冲突的时候,谁先匹配到用谁的(代码由上往下执行)
3.一般路由响应动态资源,静态资源中间件响应静态资源
防盗链:简单来说就是它可以防止外部的网站盗用本网站的资源,它的具体实现是在于判断请求头中的referer属性对应的值是否是我所规定的值,在请求发送过来的时候,使用中间件函数进行处理,在函数中判断请求头的参数是否与规定相符合,从而决定是否做放行处理
Mongodb<360>
说明: Mongodb数据库是一个基于分布式文件储存的数据库,操作的语法与js类似,对于数据库而言,其主要作用是存储数据,并对数据进行增删改查等操作,相比于用纯文件管理数据的方式来说,它具有速度更快、扩展性强和安全性强的特点,这个仓库可以理解成一个文件夹,仓库可以多个,然后每个仓库可以存放很多的集合(集合可以理解成文件夹中的文件),然后在集合中可以存放文档(它是数据库的最小单位,可以将它理解成一个js对象)
安装(官网链接): 在下载的时候注意选择zip包下载,下载完毕后将压缩包移动到c:\Program Files下再解压,之后创建C:\data\db目录,因为其数据默认保存在这里,之后以mongodb的bin目录作为工作目录,在这个目录中打开命令行,输入命令mongod,看到存在waiting for connections则表示服务已经启动
常用命令:
// 数据库部分: // 显示所有的数据库 show dbs // 切换指定的数据库(不存在会自动创建) use 数据库名 // 显示当前所在的数据库 db // 删除数据库 use 数据库 db.dropDatabase()
// 集合部分: // 创建一个集合 db.createCollection('集合名称') // 显示当前数据库所有的集合 show collections // 删除某个集合 db.集合名.drop() // 集合的重命名 db.集合名.renameCollection('新的名字')
// 文档部分: // 插入文档 db.集合名.insert(文档的对象) // 查询文档 db.集合名.find(查询条件) // 更新文档 db.集合名.update(查询条件,新的文档) // 删除文档 db.集合名.remove(查询条件)
mongoose: 这是一个插件,通过这个插件可以实现用js代码来控制Mongodb数据库,使数据库的操作更加方便。
// 数据库的连接: // 安装的话通过 npm i mongoose 命令就可以 // 导入mongoose const mongoose = require('mongoose') // 连接Mongodb服务 // mongoose.connect的方法参数是服务的URL(默认端口为27017) // 这里表示访问haha这个数据库,如果没有的话会自动创建 mongoose.connect('mongodb://127.0.0.1:27017/haha') // 设置回调函数(once只会执行一次,on的话在重新链接服务的时候会自动连接) mongoose.connection.once('open', () => {}) // 连接成功 mongoose.connection.on('error', () => {}) // 连接失败 mongoose.connection.on('close', () => {}) // 连接关闭 // 这个方法用于关闭连接Mongodb服务 // mongoose.disconnect()
// 文档的插入、删除、更新、读取 const mongoose = require('mongoose') mongoose.connect('mongodb://127.0.0.1:27017/haha') mongoose.connection.once('open', () => { // 创建文档的结构对象(设置集合中的文档的属性以及属性值的类型)(字段类型) let BookSchema = new mongoose.Schema({ name: String, age: Number }) // 创建模型对象(可以对文档进行增删改查的操作) let BookModel = mongoose.model('books', BookSchema) // 新增内容 BookModel.create({ name: '张三', age: 20 }, (err, data) => { // 函数在新增成功后会执行,err表示错误,data则表示数据 // 有错误就不会有数据 }) // 删除内容 // deleteOne表示删除一条 // deleteMany表示删除多条 BookModel.deleteOne({ // 删除的话是按条件删除的 }, (err, data) => { // 函数在删除成功后会执行,err表示错误,data则表示数据 // 有错误就不会有数据 }) // 更新内容 // updateOne表示删除一条 // updateMany表示删除多条 BookModel.updateOne({ // 按条件更新前的数据 }, { // 按条件更新后的数据 }, (err, data) => { // 函数在更新成功后会执行,err表示错误,data则表示数据 // 有错误就不会有数据 }) // 读取内容 // findOne表示读取一条 // find表示读取多条 // findbyid('id编号')表示通过id来获取 BookModel.findOne({ // 按条件读取数据 }, (err, data) => { // 函数在读取成功后会执行,err表示错误,data则表示数据 // 有错误就不会有数据 }) // 关闭建立的数据库的连接(在项目代码执行的时候这样代码不会生效) mongoose.disconnect() })
// 常见的字段类型: String:字符串 Number:数字 Boolean:布尔值 Array | []:数组 Data:日期 Buffer:buffer对象 mongoose.Schema.Types.Mixed:任意类型 mongoose.Schema.Types.Objectid:对象id mongoose.Schema.Types.Decimal128:高精度数字
// 字段的验证(举例)(在设置字段类型的时候用对配置) name:{ // 字段类型 type:String // 必填项 required:true // 默认值 default:'haha' // 可选值(enum这个字段只能填写A,B两个字母的选择) enum:['A', 'B'] // 独一无二的值(不可以重复) unique:true }
// 条件搜索: // 运算符:搜索price字段小于20的信息 // > --> $gt // < --> $lt // >= --> $gte // <= --> $lte // !== --> $ne BookModel.findOne({ price: { $lt: 20 } }) // 逻辑操作符:搜索price字段为20或21的信息 // $or --> 逻辑或 // $and --> 逻辑与 BookModel.findOne({ $or: [{ price: 20 }, { price: 21 }]})
// 条件筛选:搜索的结果只能看见自己选择的字段(1表示选择,0表示不选择) // 主要通过select方法进行设置 // 这里只能看见搜索的price字段的数据 BookModel.find().select({ price: 1 }).exec('这里设置回调函数') // 相同的操作还有排序(通过sort方法,1表示升序,0表示降序),跳过 // 数据(skip方法来设置跳过数据的条数),截取(limit方法进行数据的截取)
接口
含义: 接口是前后端通信的一个桥梁,会根据前端的需求来响应结果,结果一般是json格式的,大多数的接口是后端工程师开发的,前端工程师调用,其开发的语言没有规定,如果要实现一个接口,就需要写接口的请求方法、接口地址、请求参数以及响应的结果。
RESTful API: 它是一种特殊类型的接口,特殊在其URL中的路径表示资源,但是路径中不可以存在动词,并且操作资源的时候需要与请求方法对应起来,操作结果的时候需要与状态码对应起来
json-server: 这是一个由js编写的一个工具包,可以快速的构建RESTful API服务。
// 使用流程: // 全局安装: npm i json-server -g // 创建json的文件存储数据 { "song": [ {"id": 1} ] } // 以json文件所在的目录为工作目录,执行以下命令: json-server --watch 文件名 // 在执行完毕后终端会出现URL的地址,将地址从地址栏中输入就可以访问到数据了
会话控制
含义: 由于HTTP是一种无状态的协议,它没有办法区别请求都来自于谁,也就是无法区分用户,但是现在的产品都存在这样的需求,所以就可以通过会话控制来解决(cookie、session、token)。
cookie: cookie是HTTP服务器发送到用户浏览器并保存在本地的一块数据,它是按照域名划分保存的,其次当浏览器向服务器发送请求的时候,请求所携带的数据会被浏览器所接收,之后浏览器会在响应报文中有一个特殊的请求头set-cookie,然后在set-cookie中设置cookie的内容,当浏览器解析到请求头中的set-cookie的时候,它会将cookie储存在当前域名下,然后下次向服务器发送请求的时候会自动携带当前域名下面的可用的cookie,通过请求报文中的cookie的请求头传递给服务器,被服务器解析到后,响应响应的内容。
// cookie在express框架中的操作 const express = require('express') const app = express() app.get('/', (request, response) => { // cookie的设置: // request.cookie('设置cookie的名字','cookie的内容',{ 配置对象 }) // maxAge表示cookie过期时间的设置,单位为毫秒 request.cookie('哈哈哈','123',{ maxAge:60 }) // 删除cookie: request.clearCookie('需要删除的cookie的名字') request.end('hello about') })
session: 它是保存在服务器中的数据(当前用户访问的相关的信息),用它可以用来识别用户的身份,快速获取当前用户信息的相关操作,另外,当用户的信息发送给服务器的时候,服务器会进行校验,正确的话会创建一个session对象用来保存当前用户的基本信息,在这个对象中存在一个session_id的标识(独一无二),服务器以响应cookie的方式响应给浏览器,浏览器会将这个cookie进行存储,然后根据这个cookie进行判断以及内容的返回。
// JWT的使用: // JWT可以用来创建token以及校验token // 需要下载jsonwebtoken这个包 // 导入 const jwt = require('jsonwebtoken') // token的生成 // 使用方法jwt.sign('数据',需要加密的字符串,配置对象) // token的校验 // 使用方法jwt.verify('校验的token',生成token的加密字符串,回调函数)
Token: 它是服务端生成一串加密的字符串,字符串中保存用户的信息,其主要用在移动端中,在用户信息完成校验之后服务器会创建并返回token,下次请求时会将token发送给服务器,给服务器解析出用户的信息并做相应的处理,从而识别身份,但是token需要手动进行携带,对于token而言,它使用起来服务端压力小(数据存储在客户端)、相对安全(数据加密,可以避免CSRF攻击)、扩展性强(服务间可以共享)
发表评论 取消回复