这是我第三次对node.js进行学习,第一次是在校的大二下学期
,因为自己能力的局限性,在B站看完全部教学视频自己也有使用node.js写api接口,所掌握的知识点十分有限。第二次是大三的寒假
对node.js进行了二刷,对知识的理解也比第一次更深刻。这次是笔者大三下学期
,此时的我已经在公司里实习了,因为项目中的服务器端使用的就是node.js,leader也分配了一些设计api接口的任务。因此借五一假期
进行三刷,同时写下本编文章,记录自己的学习路程。时间真快,最后,祝自己能找到一份理想的工作。
随着时代的发展,前端程序员必备的开发知识也不仅局限于HTML
、CSS
、Javascript
、前端框架如Vue、React、Angular
等了。node.js
也成为了一项必备的技能之一,相信接触过招聘网站的同学都知道,十个前端岗位要求中,超过一半是需要具备服务器端开发语言的,其中node.js
就属于其中,其必要性可想而知。
以下是对node.js官网的一句原话。翻译过来的意思就是:Node.js是一个开源的,跨平台的JavaScript运行环境。通俗点来说:Node.js就是一款应用程序,是一款软件,它可以运行JavaScript。
Node.js@ is an open-source, cross-platform JavaScript runtime enviroment.
注意:
在node.js中是不能操作DOM和BOM的,因为node.js中的JavaScript组成部分是由ECMAScript组成和Node API组成,JavaScript则是由ECAMScript、Web API组成,要注意区分二者的区别。
小结
Buffer 中文译为[缓冲区],是一个类似于 Array 的对象,用于表示固定长度的字节序列
换句话说,Buffer 就是一段固定长度的内存空间,用于处理二进制数据
创建
buffer的创建有三种方式,分别为alloc、allocUnsafe、from
// 1. alloc
let buf = Buffer.alloc(10);
console.log(buf); //
// 2.allocUnsafe 可能会包含旧的内存数据,速度相比alloc较快些
let buf2 = Buffer.allocUnsafe(10);
console.log(buf); //
// 3. from 该方法会将参数转化为对应的Unicode(ASCII)
let buf3 = Buffer.from("hello");
console.log(buf3) //
转换
Buffer的可阅读性没有那么强,我们可以使用toString()方法的调用来将数据Buffer流转换为字符串,提高可阅读性。
let buf = Buffer.from("hello");
let str = buf.toString(); // hello
修改
在buffer中可以使用访问数据元素的方式进行buffer对应下标的数据访问,如buffer[0]、buffer[1]、buffer[2]…,也可以使用赋值的方式进行修改buffer[0] = 98(Unicode)
let buffer = Buffer.from('hello')
console.log(buffer) //
// 修改buffer[0]
buffer[0] = 95;
console.log(buffer) //
溢出
计算机中8个字位的二进制存储只能存储十进制的255,即最大十进制数据就是255,超过后就会溢出。在buffer中,当二进制溢出时会舍弃最高位的数字。举个例子:buffer[0] = 361 361转换位二进制为:0001 0110 1001,因为只能存储八位因此存储的结果为0110 1001。
中文
在buffer中,一个中文占3个字节
let buf = Buffer.from('你好');
console.log(buf); //
file system:fs模块可以实现与硬盘的交互,例如:文件的创建、删除、重命名、移动,还有文件内容的写入、读取,以及文件夹的相关操作。
在node.js中,我们可以使用require的方式将
fs
模块导入。
写入
方法 | 说明 |
---|---|
writeFile | 异步写入 |
writeFileSync | 同步写入 |
createWriteStream | 流式写入 |
在
fs
模块中有一个writeFile(异步)/ writeFileSync(同步)方法,可以创建文件并写入内容。
/*
* fs.writeFile(File, data[, option],callback)
* @param: file 文件名(必选)
* @param: data 待写入的内容(必须)
* @param: option 选项配置(可选),表示以什么格式方式写入文件,默认值是utf8
* @param:callback 回调函数(必选)
*/
const fs = require('fs')
fs.writeFile('../files/write.txt', '写入内容', (err, data) => {
if (err) {
console.log('写入文件失败' + err.message);
return;
}
console.log(data);
return;
})
文件的写入还有第二种方式就是使用createWriteStream流式写入,
程序打开一个文件是需要消耗资源的
,流式写入可以减少打开关闭文件的次数。流式写入方式适用于大文件写入或者频繁写入的场景,而writeFilefont适合于写入频繁较低的场景
const fs = require('fs');
const ws = fs.createWriteStream('../files/观书有感.txt');
ws.write('半亩方塘一鉴开\r\n');
ws.write('天光云影共徘徊\r\n');
ws.write('问渠那得清如许\r\n');
ws.write('为有源头活水来\r\n');
ws.close();
文件写入的场景:
当需要持久化保存数据的时候,应该想到文件写入。
追加
在
fs
模块中有一个appendFile(异步) / appendFileSync(同步)方法,可以创建文件并追加内容
/*
* fs.appendFile(File, data[, option],callback)
* @param: file 文件名(必选)
* @param: data 待写入的内容(必须)
* @param: option 选项配置(可选),表示以什么格式方式写入文件,默认值是utf8
* @param:callback 回调函数(必选)
* return: undefined
*/
const fs = require('fs');
fs.appendFile('../files/write.txt', "\r\n我是追加的内容", (err, res) => {
if (err) {
return console.log('错误', err.message)
}
console.log(res);
return;
})
读取
方法 | 说明 |
---|---|
readFile | 异步读取 |
readFileSync | 同步读取 |
createReadStream | 流式读取 |
在
fs
模块中有一个readFile(异步)/ readFileSync(同步)方法,可以读取文件内容。
/* readFile(path[, option], callback)
* @param: path(必选) 文件路径
* @param:option(可选) 读取文件的编码格式
* @param:callback(必选)回调函数
* return: undefined
*/
const fs = require('fs')
// 异步读取
fs.readFile('../结项总结.txt', 'utf8', (err, data) => {
if (err) {
console.log('读取文件失败!' + err.message);
return
}
console.log('读取文件成功!' + data);
return
})
// 同步读取
let data = fs.readFileSync('../结项总结.txt');
console.log(data.toString());
// 创建流式读取对象
const rs = fs.createReadStream(path);
// 绑定data事件,chunk
rs.on('data', chunk => {
})
// end 可选事件,当数据读取结束后自动执行
rs.on('end', ()=> {
console.log('读取完成');
})
文件读取的场景:
移动
在Node.js中,我们可以使用rename或者renameSync来移动或者重命名文件或者文件夹
语法:
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
参数说明:
- oldPath:文件当前的额路径
- newPath:文件新的路径
- callback:操作后的回调函数
const fs = require('fs');
// rename方法
fs.rename('../files/观书有感.txt', './观书有感.txt', err => {
if (err) return console.log(err.message);
console.log("重命名成功!");
})
// renameSync方法
fs.renameSync('./观书有感.txt', '../files/观书有感.txt');
删除
在Node.js中,我们可以使用unlink或者unlinkSync来删除文件
语法:
fs.unlink(path, callback)
fs.unlinkSync(path)
参数说明:
- path:文件路径
- callback:操作后的回调
const fs = require('fs');
// unlink
fs.unlink('../files/test.txt', err => {
if (err) return console.log(err.message);
console.log("文件删除成功...");
})
// unlinkSync
fs.unlink('../files/test.txt');
操作文件夹
在node.js中,我们以对文件夹进行以下的操作
方法 | 说明 |
---|---|
mkdir / mkdirSync | 创建文件夹 |
readdir / readdirSync | 读取文件夹 |
rmdir / rmdirSync | 删除文件夹 |
创建
在Node.js中,我们可以使用mkdir或者mkdirSync来创建文件夹
语法:
fs.mkdir(path[, option], callback)
fs.mkdirSync(path[, option])
参数说明:
- path:文件夹路径
- option:选项配置(可选)
- callback:操作后的回调
**其中:**当我们需要将path
进行递归生成子目录时,可以在option
中添加{recursive:true}
读取
在Node.js中,我们可以使用mkdir或者mkdirSync来创建文件夹
语法:
fs.readdir(path[, option], callback)
fs.readdirSync(path[, option])
参数说明:
删除
在Node.js中,我们可以使用mkdir或者mkdirSync来创建文件夹
语法:
fs.rmdir(path[, option], callback)
fs.rmdirSync(path[, option])
参数说明:
**其中:**当我们需要将path
进行递归删除子目录时,可以在option
中添加{recursive:true}
,但是node环境会提示使用fs.rm()。
path模块提供了操作路径的功能,我们将介绍如下几个较为常用的几个API:
API | 说明 |
---|---|
path.resolve | 拼接规范的绝对路径 |
path.sep | 获取操作系统的路径分隔符 |
path.parse | 解析路径并返回对象 |
path.basename | 获取路径的基础名称 |
path.dirname | 获取路径的目录名 |
path.extname | 获取路径的扩展名 |
请求报文由三部分组成,分别是请求行、请求头、请求体。
请求行
请求方法:
方法 | 作用 |
---|---|
GET | 主要用于获取数据 |
POST | 主要用于新增数据 |
PUT / PATCH | 主要用于更新数据 |
DELETE | 主要用于删除数据 |
HEAD / OPTIONS / CONNECT / TRACE | 使用相对较少 |
url(Uniform Resource Locator):统一资源定位符,本身也是一个字符串。
HTTP版本
版本号 | 发布时间 |
---|---|
1.0 | 1996年 |
1.1 | 1999年 |
2 | 2015年 |
3 | 2018年 |
请求头
请求头都是以
键值对
的形式出现的,记录了浏览器的一些相关信息。
请求体
请求体的内容由开发者决定,多个请求内容由&连接。
响应报文主要由三部分组成:响应行、响应头、响应体。我们可以观察到
请求报文
和响应报文
的组成结构都是大同小异的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qlDUropy-1682844650973)(E:\node图片\响应报文.png)]
响应行:主要由HTTP版本号、响应状态码、状态码含义组成。
IP本质上是一个
32bit
的二进制字
符串,将每8bit
进行拆分,得到IPV4
的形式的十进制
字符串,同时使用.
进行分割,这样就形成了日常我们所接触到的ip形式。
在Node.js中,可以使用引入http模块的方式来创建一个本地服务器。
注意:
- 在使用http模块时,响应体需要返回中文,但没有设置对应的header时,浏览器收到的数据会乱码
创建
// 1.导入http模块
const http = require('http');
const path = require("path")
// 2.调用createServer,创建http实例
const server = http.createServer((request, response) => {
// request:请求报文 、 response:响应报文
});
// 3.使用on方法为http实例绑定request事件
server.on('request', (req, res) => {
res.setHeader('Content-Type', 'text/html;charset=utf-8')
let url = req.url
switch (url) {
case '/api': res.end(JSON.stringify({
method: req.method,
msg: "请求成功",
status: 200,
}))
break;
default:
res.end("404")
break;
}
// 3.1 设置响应头,解决中文乱码问题
res.setHeader('Content-Type', 'text/html;charset=utf-8');
})
// 4.开启服务器
server.listen(8080, () => {
console.log("serve running");
})
获取请求头
想要获取请求的数据,需要通过request对象。
含义 | 语法 | 重点掌握 |
---|---|---|
请求方法 | request.method | * |
请求版本 | request.httpVersion | |
请求路径 | request.url | * |
URL路径 | require(“url”).parse(request.url).pathname | * |
URL查询字符串 | require(“url”).parse(request.url, true).query | * |
请求头 | request.headers | * |
请求体 | request.on(“data”, chunk => {}) request.on(“end”, ()=>{}) |
注意事项:
- request.url只能获取路径以及查询字符串,无法获取URL中的域名以及协议的内容
- request.headers将请求信息转化为一个对象,并将对象的属性名都转化为了[小写]
- 关于路径:如果访问网站的时候,只填写了IP地址或者域名信息,此时请求的路径为[/]
- 关于favicon.ico:这个请求是属于浏览器自动发送的请求
DEMO
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
let { method } = req;
let { pathname } = new URL(req.url, "http://127.0.0.1:8000");
res.setHeader("Content-Type", "text/html;charset=utf-8");
if (method === 'POST' && pathname === '/login') {
return res.end("登录页面")
} else if (method === 'POST' && pathname === '/register') {
return res.end("注册页面")
};
return res.end('404 not found');
})
server.listen(8000, () => {
console.log("8000端口监听...");
})
设置响应报文
我们可以通过回调函数中callback的response参数设置响应报文。
设置响应状态码:response.statusCode = 具体数值
设置响应状态码描述内容:response.statusMessage = 字符串(不能为中文)
设置响应头:response.setHeaders(‘key’, ‘value’)
设置响应体:response.write() / response.end(),前者可以调用多次,后者只能调用一次
mime类型
媒体类型(通常称为Multipurpose Internet Mail Extensions 或者 MIME类型)是一种标准,用来表示文档、文件或者字节流的性质和格式
mime
类型结构: [type] / [subType]
例如
:text / html text / csss image / jpeg image / png application / jsonHTTP服务可以设置响应头Content-Type来明确响应体的MIME类型,浏览器会根据类型决定如何处理资源
下面是常见文件对应的minme类型
html:’text/html‘,
css:’text/css‘,
js:’text/javascript‘
…
对于未知的资源类型,可以选择application/octet-stream类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的
下载
效果。
Get和POST请求
GET请求的情况:
- 在地址栏直接输入url访问
- 点击a链接
- link标签引入css
- script标签引入js
- video与audio要引入多媒体
- img标签引入图片
- form标签的中的method为GET(不区分大小写)
- ajax中的GET请求
POST请求的情况:
- form标签中的method为POST(不区分大小写)
- AJAX的POST请求
GET和POST请求的区别
GET和POST是HTTP协议请求的两种方式,主要有如下几个区别
- 作用。GET主要用于获取数据,POST主要用于提交数据
- 参数位置。GET带参数请求是将参数缀到URL之后,POST带参数请求时将参数放到请求体中
- 安全性。POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏
- GET请求大小有限制,一般为2k,而POST请求则没有限制
什么是模块化与模块 ?
将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化
其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用
什么是模块化项目 ?
编码时是按照模块一个一个编码的,整个项目就是一个模块化的项目
模块化好处
- 防止命名冲突
- 高复用性
- 高维护性
在Node.js中,遵循的是
CommonJS
规范。可以使用exports
和module.exports
的将变量或者函数暴露
的方式实现模块化。相同的,如果某个文件需要用到模块化中的数据时,可以使用require
的方式将所需要的数据进行引入。模块暴露数据的方式有两种:
module.exports = value
exports.name = value
使用时有几点注意:
- module.exports可以暴露任意数据
- 不能使用 exports = value 的形式暴露数据,模块内部module 与 exports 的隐式关系 exports =module .exports ={}
require
返回的结果是module.exports
中的值
// 01.m1.js文件如下
console.log('加载了01.m1.js');
const name = 'eason';
exports = name; // 这种暴露的方法是错误的
module.exports = { name };
// 02.m2.js文件
const m1 = require('./01.m1') // 注意加载可以省略后缀名
console.log(m1.name); // 输入eason
在模块中使用 require 传入文件路径即可引入文件
const test = require('./me.js');
require 使用的一些注意事项
对于自己创建的模块,导入时路径建议写相对路径 ,且不能省略 ./ 和 …/
js 和 json 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到
如果导入其他类型的文件,会以 js 文件进行处理
如果导入的路径是个文件夹,则会首先检测该文件夹下 package.json 文件中 main 属性对应的文件如果 main 属性不存在,或者 package.json 不存在,则会检测文件夹下的 index.js 和 index.json如果还是没找到,就会报错
导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./和…
require 还有一种使用场景,会在 包管理工具 节介绍
module.exports 、exports 以及 require 这些都是 CommonJS 模块化规范中的内容而 Node.js 实现了 CommonJS 模块化规范
介绍一下require导入的
自定义模块
的基本流程
- 将相对路径转为绝对路径,定位目标文件
- 缓存检测
- 读取目标代码
- 包裹一个函数并执行(自执行函数)。通过
arguments.callee.toString()
查看执行函数- 缓存模块的值
- 返回
module.exports
的值
- 包是什么?
[包] 英文单词是 package,代表了一组特定功能的源码集合
- 包管理工具
管理[包]的应用软件,可以对[包] 进行 下载安装 , 更新, 删除 , 上传 等操作
借助包管理工具,可以快速开发项目,提升开发效率
包管理工具是一个通用的概念,很多编程语言都有包管理工具,所以 掌握好包管理工具非常重要
- 常用的包管理工具
下面列举了前端常用的包管理工具
- npm
- yarn
- cnpm
npm 全称 Node Package Manager ,翻译为中文意思是 [Node 的包管理工具Jnpm 是 node.js 官方内置
包管理工具,是必须要掌握住的工具
- npm 的安装
node.js 在安装时会自动安装 npm ,所以如果你已经安装了 node.js,可以直接使用 npm可以通过 npm -v
查看版本时可能与上图版本号不一样,不过不影响正常使用
npm init
npm init 命令的作用是将文件夹初始化为一个[包],交互式创建 package.json 文件
package.json 是包的配置文件,每个包都必须要有 package.json
{
"name":“1-npm” #包的名字
"version":"1.0.0" #包的版本
"description": "" #包的描述
"main":"index.js", #包的入口文件
"scripts":{ #脚本配置
"test":"echo \"Error: no test specified\" && exit 1"
}
"author": #作者
"license":"ISC" #开源证书
}
初始化的过程中还有一些注意事项
- package name(包名)不能使用中文、大写,默认值是 文件夹的名称,所以文件夹名称也不能使用中文和大写
- version(版本号)要求 x.x.x 的形式定义,x 必须是数字,默认值是 1.0.0
- ISC证书与 MIT 证书功能上是相同的,关于开源证书扩展阅读
- package.json 可以手动创建与修改
- 使用 npm init -y 或者 npm init --yes 极速创建 package.json
我们可以通过 npm install 和 npm i 命令安装包,其中使用npm i <包名>@指定版本可以实现安装指定版本的依赖包。同时 npm i -g <包名>@指定版本 npm i在i 后添加
-g
的命令可以实现全局安装
,此时安装的依赖包会被安装到本地计算机中
。**注意:**window系统下默认是不允许安装全局脚本的,因此需要使用管理员身份运行命令行窗口
,执行该行命令set-ExecutionPolicy remoteSigned
,选Y
即可。可以使用npm i <包名> <包名> <包名>…一次性安装多个依赖包。
npm install <包名>
npm i <包名>
npm i <包名>@指定版本 # 安装指定版本包
npm i -g <包名>@指定版本 # 全局安装包
npm i <包名> <包名> <包名>... #安装多个依赖包
示例
npm install uniq
npm i unig
运行之后文件夹下会增加两个资源
node_modules 文件夹 存放下载的包
package-lock.ison 包的锁文件 ,用来锁定包的版本
安装 unig 之后,unig 就是当前这个包的一个依赖包,有时会简称为 依赖比如我们创建一个包名字为A,A中安装了包名字是 B,我们就说 B 是 A 的一个依赖包,也会说 A 依赖 B
在npm中使用npm uninstall <包名>即可将依赖包进行删除
npm uninstall <包名>
开发环境是程序员 专门用来写代码 的环境,一般是指程序员的电脑,开发环境的项目一般 只能程序员自己
访问
生产环境是项目 代码正式运行 的环境,一般是指正式的服务器电脑,生产环境的项目一般 每个客户都可以访问
生产依赖与开发依赖
我们可以在安装时设置选项来区分 依赖的类型,目前分为两类
类型 | 命令 | 补充 |
---|---|---|
生产依赖 | npm i -S unig npm i --save uniq |
-S等效于 --save,-S是默认选项 包信息保存在 package.json 中 dependencies 属性 |
开发依赖 | npm i -D less npmi–save-devrless |
-D等效于 --save-dev 包信息保存在package.json 中 devDependencies 属性 |
通过配置命令别名可以更简单的执行命令
配置 package.json中的 scripts 属性
{
'scripts":{
"server":"node server .js"
"start":"node index.js",
}
}
配置完成之后,可以使用别名执行命令
npm run server
npm run start
不过 start 别名比较特别,使用时可以省略 run
npm start
补充说明:
npm start 是项目中常用的一个命令,一般用来启动项目.
npm run 有自动向上级目录查找的特性,跟 require 函数也一样
对于陌生的项目,我们可以通过查看 scrIpts 属性来参考项目的一些操作