一.前端时代
1.前端的发展史
- 工程化 ng webpack glup
- 全栈时代 (现在处在这个中期)
- 云 + 端时代 + 体验 微信云开发
2.javascript 和 浏览器环境介绍
2.1.浏览器中javascript的组成部分
2.2 Javascript 既能做前端开发也能做后端开发
1.做后端开发借助node.js的运行环境
2.做前端开发借助浏览器的运行环境
二.node介绍
1.1. node.js是⼀个异步的事件驱动的JavaScript运⾏环境(基于 Chrome V8 引擎)
注意:浏览器是JS的前端运行环境,node是js的后端运行环境
1.2. node为性能⽽⽣
- 多进程- C Apache
- 多线程- java
- 异步IO - js
- 协程- lua openresty go deno- go TS
1.3. 与前端的不同
- JS核⼼语法不变
- 前端BOM DOM
- 后端fs http bufer event os
1.4 node可以做任何你想做的 只要你有想法
三.node学习模块
1.基础
2.Koa
3.网络编程
4/5.持久化
6.鉴权
7.eggjs MVC 分层原理
8.eggjs更加实践
9.TypeScript与装饰器
10.部署 Docker 自动化部署
1.基础
1.1.node命令
1. node -v
2. node + tab键 //快速补全路径(需要先敲出文件的首字母)
3. esc键 //敲错命令后快速清除
4. cls //清空当前终端输入的命令
1.2.常用模块重
- 核心模块(不用require引直接使用)
- 内置模块(可以直接require引入使用 不需要install)
- 第三方模块 如download-git-repo (需要install和require引入使用)
1.3.nodemon
nodemon 运行js 会监听js的改变并打印不同结果,node运行js修改后则需要重启node
//nodemon 运行js
npm install -g nodemon
nodemon xx.js //监听改变
node xx.js //改变后需要重启
1.4 内置API
1.fs系统模块
1.1 读取指定文件的内容
fs.readFile()
1.2 向指定文件中写入内容 fs.wrireFile()
fs案例:
(1). 成绩写入功能
//将 小红=99 小白=100 小黄=70 小黑=66 小绿=88 转换成
//小红:99
//小白:100
//小黄:70
//小黑:66
//小绿:88 格式后 写入新的文件
const fs = require("fs");
fs.readFile("./成绩.txt", "utf8", function (err, dataStr) {
if (err) {
console.log("文件读取失败" + err.message);
return;
}
console.log(dataStr);
let dataArr = dataStr.split(" ");
let newArr = [];
dataArr.forEach((ele, i) => {
let txt = ele.replace("=", ":");
newArr.push(txt);
});
const newStr = newArr.join("\r\n");
fs.writeFile("./成绩-ok.txt", newStr, function (err, dataStr) {
if (err) {
return console.log("写入失败" + err.message);
}
console.log("写入文件成功");
});
});
(2).fs-模块-路径动态拼接
- 在fs操作模块如果提供 ./或../的
相对路径很容易出现路径动态拼接问题
- 原因:代码运行时,
会执行node命令所在的目录,动态拼接出被操作的文件完整路径 - 解决方案:在fs操作文件时,
使用完整路径,不用./相对路径,防止动态拼接__dirname 表示当前文件所处的目录
const fs = require("fs");
fs.readFile(__dirname + "/成绩.txt", "utf8", function (err, dataStr) {
if (err) {
return console.log("文件读取失败");
}
console.log("文件读取成功", dataStr);
});
2.path 路径模块
1.path.join()的语法格式
注意:凡是拼接路径的都是用path.join()来处理
//1.paths<string>路径片段的序列
//2.返回值:<string>
path.join([...paths])
2.
path.basename()方法获取路径的最后一部分
//1.path<string>必选参数,表示一个路径的字符串
//2.ext<string>可选参数,表示文件扩展名
//3.返回<string>表示路径中的最后一部分
path.basename(path[,ext])
3.path.extname()获取路径中的文件扩展名
//1.path<string>必选参数,表示一个路径的字符串
//2.返回<string>返回得到的扩展名字字符串
path.extname(path)
案例:fs加path综合案例:
注意:1.fs.writeFile()只能创建文件,不能用来创建路径。2.重复调用fs.writeFile()写入同一个文件新内容会替换旧内容
- 将index.html拆分成3个文件:(1)index.css (2)index.js (3)index.html 并将拆分出来的文件,存放到file目录中,运行的结果和原来index.html一样
const path = require("path");
const fs = require("fs");
//匹配<style></style>标签正则
//其中\s表示空白字符 \S表示非空白字符 *表示匹配任意次
const regStyle = /<style>[\s\S]*<\/style>/;
//匹配<script></script>标签正则
const regScript = /<script>[\s\S]*<\/script>/;
fs.readFile(
path.join(__dirname, "./index.html"),
"utf8",
function (err, dataStr) {
if (err) {
console.log("文件读取失败", err.message);
}
resolveCss(dataStr);
resolveJs(dataStr);
resolveHtml(dataStr);
}
);
//exec()方法用于检索字符串中的正则表达式匹配, 匹配成功返回数组否则返回null
function resolveCss(htmlStr) {
const rl = regStyle.exec(htmlStr);
const newCss = rl[0].replace("<style>", "").replace("</style>", "");
fs.writeFile(
path.join(__dirname, "./file/index.css"),
newCss,
function (err, dataStr) {
if (err) {
return console.log("文件写入失败", err.message);
}
console.log("写入css成功");
}
);
}
function resolveJs(htmlStr) {
const rl = regScript.exec(htmlStr);
const newJs = rl[0].replace("<script>", "").replace("</script>", "");
fs.writeFile(
path.join(__dirname, "./file/index.js"),
newJs,
function (err, dataStr) {
if (err) {
console.log("js写入失败" + err.message);
}
console.log("js写入成功");
}
);
}
function resolveHtml(htmlStr) {
const newHtml = htmlStr
.replace(regStyle, '<link rel="stylesheet" href="./index.css"/>')
.replace(regScript, '<script src="./index.js"></script>');
fs.writeFile(
path.join(__dirname, "./file/index.html"),
newHtml,
function (err, dataStr) {
if (err) {
console.log("html写入失败");
}
console.log("html写入成功");
}
);
}
3.http模块
- 什么是http模块
解释:在网络节点中负责消费资源的电脑叫客户端;负责提供网络资源的电脑叫服务器
服务器和普通电脑的区别在于,服务器上安装了web服务器软件,例如IIS,Apache等通过安装这些服务器软件,就能把一台普通电脑变成一台web服务器
const http = require('http')
nodeJs通过http.createServer()方法旧能把一台普通的电脑变成web服务器
- IP地址
说明:ip地址就是互联网上
每台计算机的唯一地址,类似电话号码
格式:点分十进制表示成(a.b.c.d)的形式。abcd都是在0-255之间的十进制数例如:192.168.1.1 注意:
(1).互联网中每台web服务器,都有自己的IP地址,eg:在window终端运行 ping www.baidu.com 命令查百度的ip地址
(2).127.0.0.1把自己电脑当作一台服务器访问
- 域名和域名服务器
说明:ip地址和域名是
一一对应的关系(类似每个人身份证号和姓名的关系),域名是方便人们记忆。这种对应关系存放在了域名服务器(DNS)的电脑中
百度域名:www.baidu.com ip地址 186.61.200.6
本机域名:localhost IP地址:127.0.0.1
- 端口号 说明:类似门牌号,通过门牌号找到整栋大楼众多房间中的某个房间
- 创建web服务器的基本步骤
1.导入http模块
const http = require('http')
2.创建web服务器实例
const server = http.createServer()
3.微服务器实例绑定request事件,监听客户端的请求
server.on('request',(req,res)=>{
//只要客户端请求自己的服务器就会触发request事件
//req对象存放与客户端相关的数据和属性
//req.url 请求的url地址
//req.method 请求的类型
//res响应对象访问与服务器相关的数据或属性
const str = `url ${req.url} methods ${req.method} 乱码吗?`
//防止中文乱码 需要设置响应头
res.setHeader("Content-Type", "text/html;charset=utf-8");
//res.end()方法向客户端发送指定内容,并结束这次请求处理过程
res.end(str)
console.log('web server')
})
4.启动服务器
server.listen(80,()=>{
console.log('run at 127.0.0.1')
})
- 根据不同url响应不同的html内容
const http = require("http");
const server = http.createServer();
server.on("request", (req, res) => {
let content = "<h1>404,页面出错啦</h1>";
if (req.url === "/" || req.url === "/index.html") {
content = "<h1>首页</h1>";
} else if (req.url === "/about.html") {
content = "<h1>首about 页面</h1>";
}
res.setHeader("Content-Type", "text/html;charset=utf-8");
res.end(content);
});
server.listen(8090, () => {
console.log("服务启动成功");
});
- 案例 实现本地html放入web服务器
//1.导入模块
const http = require("http");
const fs = require("fs");
const path = require("path");
//2.创建web服务器
const server = http.createServer();
//3.监听服务器的改变
server.on("request", (req, res) => {
//对路径做一下映射
//let fPath = path.join(__dirname, req.url);
//对路径做一下处理 直接访问127.0.0.1可以拿到页面
//获取客户端的页面地址
let fPath = "";
if (req.url === "/") {
fPath = path.join(__dirname, "./index.html");
} else {
fPath = path.join(__dirname, "./file", req.url);
}
res.setHeader("Content-Type", "text/html;charset=utf-8");
//读取客户端文件
fs.readFile(fPath, "utf8", (err, dataStr) => {
if (err) {
res.end("404 NOT fount");
return;
}
//发送到服务器页面
res.end(dataStr);
});
});
server.listen(8090, () => {
console.log("服务启动成功");
});
4.模块化 module
- 编程中的模块化,遵循
固定规则,把一个大文件拆成独立并相互依赖的多个小模块 好处:提高代码复用性,可维护性实现按需加载 - nodeJs中的模块化
- 使用
require()方法 加载模块注意:使用require()加载其他模块时,会执行被加载模块中的代码
//1.加载内置模块
const fs = require('fs')
//2.加载用户自定义模块 方法中的 .js后缀可以被省略
const custom = require('./custom')
//3.加载第三方模块
const moment = require('moment')
- 模块作用域
好处:(1).防止作用域全局污染
module对象
注意: node.js遵循了CommomJs模块化规范,CommonJs规定了模块的特性和各模块间的相互依赖
总结:
1.module.exports 和 exports 导出 指向同一个对象的地址,但require时得到的永远是 module.exports指向的对象
2.nodeJs中module.exports 导出和 require()引入 类似 es6中 export导出,import导入,都是实现模块化
3.因为CommonJS的require语法是同步的,导致CommonJS只适合用于服务端;
而ES6模块在浏览器端和服务器端都是可用的,但是在服务端需要遵循特殊的规则
4.关于互相引用问题,ES6模块中支持加载CommonJS;而CommonJS不支持引用ES6模块
5.CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用
5.npm与包或者叫第三方模块
nodeJs中第三方模块 也 称为包,两个是相等的概念
注意:如何判断包是放在devDependencies还是dependencies中,需要进包npm官网查看安装命令
6.自定义模块
- 开发属于自己的包
- package.json介绍
{
"name": "packge",//包的下载名称,包名唯一性
"version": "1.0.0",//包的版本号
"main": "index.js",//包的入口文件
"license": "MIT",//遵循的官方协议
"discription":"包的描述信息",
"keywords":["pack","chart","echart"],//搜索关键字
}
-
注册npm账号
-
模块的加载机制
模块在第一次加载后会被缓存。意味着多次调用require()不会导致模块代码被执行多次
注意:不论内置模块,用户自定义模块,还是第三方模块,他们都优先从缓存中加载,从而提高模块的加载效率
1.内置模块的加载机制:内置模块加载`优先级最高`
2.自定义模块的加载机制:
(1)自定义模块必须以./或../开头的路径,否则会被node当成内置模块或第三方模块加载
(2)在使用require导入自定义模块,如果省略了文件名,则加载顺序为
【确切的文件名\补全.js\补全.json\补全.node\都没找到报终端加载失败】
3.第三方模块的加载机制:
(1)会在当前模块的父目录开始尝试从node_modules文件夹中加载第三方模块
(2)如果没有找到对应的第三方模块,则移动到再上一层的父目录中,进行加载,直到文件系统的根目录
目录作为模块的加载机制
2.Express
graph TD
学习目录 --> 1.初识Express
学习目录 --> 2.Express路由
学习目录 --> 3.Express中间件
学习目录 --> 4.使用Express写接口
学习目标
2.1 初始Express
-
什么时Express 1.Express是基于Node.js平台,
快速、开放、极简的web开发框架
2.通俗理解:Express的作用和node.js内置的http模块类似,是专门创建web服务器
3.Express的本质就是一个npm第三方包(是基于内置http模块进一步封装出来的),提供了快速创建web的便捷方法 -
Express能做什么 意义:方便快捷的创建
web网站(对外提供网页资源服务器)或API接口(对外提供API接口服务器) -
基本用法 (1)安装
//在项目所处的目录中
npm i express@4.17.1
(2) 创建基本的web服务器
//1.导入express
const express = require("express");
//2.创建web服务器
const app = express();
//3.启动web服务器
app.listen(80, () => {
console.log("创建127.0.0.1服务");
});
(3) 监听GET或POST请求
//1.导入express
const express = require("express");
//2.创建web服务器
const app = express();
//4.监听客户端的GET和POST请求,并向客户端响应具体内容
app.get("/user", (req, res) => {
//!req请求对象
//!res响应对象
//调用express提供的res.send()方法,向客户端响应一个json对象
res.send({
name: "张攀",
age: 18,
gender: "男",
});
});
app.post("/user", (req, res) => {
//res.send()方法,向客户端提供文本
res.send("请求成功");
});
//3.启动web服务器
app.listen(80, () => {
console.log("创建127.0.0.1服务");
});
(4)获取URL中携带的参数
(5) 获取URL中的
动态参数(可有多个动态参数)
- 托管静态资源
2.2 Express路由
(1)概念
(2)Express中路由的例子
//匹配 GET 请求 且请求url 为 /
app.get('/',function(req,res)=>{
res.send("Hello world")
})
//匹配 POST 请求 且请求url 为 /
app.post('/',function(req,res)=>{
res.send("Hello world")
})
2.3 Express中间件
(1)中间件调用流程
next函数是实现多个中间件来连续调用的关键,它表示把流转关系转交给下一个中间件或路由
(2)中间件格式
(3)定义中间件函数
//常量mw 所指向的就是一个中间件函数
const mw = function(req,res,next){
console,log('这是一个简单的中间件函数')
//注意:当前中间件的业务处理完毕后,必须调用next()函数
//表示把流转关系交给下一个中间件或路由
next()
}
(4)全局生效的中间件
客户端发起任何请求,到达服务器后,都会触发的中间件,叫全局生效的中间件
通过调用app.use(中间件函数),即可定义一个全局生效的中间件
const mw = function(req,res,next){
console,log('这是一个简单的中间件函数')
next()
}
//将mw注册为全局生效的中间件
app.use(mw)
//定义简化的全局生效的中间件
app.use((req, res, next) => {
console.log("定义简化的中间件");
next();
});
(5)中间件的作用
app.use((req, res, next) => {
//获取请求到达服务器的时间
const time = Date.now();
//为req挂载自定义属性,从而把时间共享给后面的所有路由
req.startTime = time;
next();
});
app.get("/", (req, res) => {
res.send(`home 页面 时间:${req.startTime}`);
});
(6)定义多个全局中间件
(7)局部生效的中间件
(8)定义多个局部中间件
//以下两种写法完全等价
1.app.get('/',mw1,mw2,(req,res)=>{ res.send('Home Page') })
2.app.get('/',[mw1,mw2],(req,res)=>{ res.send('Home Page') })
(9)中间件5个注意事项
2.4 Express中间件的分类
应用级别中间件路由级别中间件错误级别中间件 (放在所有路由请求之后)
const express = require("express");
const app = express();
app.get("/", (req, res) => {
throw new Error("服务器发生内部错误");
res.send("home page");
});
//定义错误级别中间件,捕获整个项目的异常错误,从而防止程序崩溃
app.use((err, req, res, next) => {
console.log("发生了错误" + err.message);
res.send(err.message + 33);
});
app.listen(80, () => {
console.log("启动服务了");
});
Express 内置中间件4.1
express.json()使用
在postman中模拟JSON格式 发送请求
const express = require("express");
const app = express();
//通过express.json()这个中间件,解析表单中的JSON格式数据
//如果不写express.json()这个中间件 则下面req.body为undefined
app.use(express.json());
app.post("/user", (req, res) => {
//在服务器可以使用req.body 这个属性来接收客户端发送过来的请求体数据
//默认情况下 如果不配置解析表单数据的中间件,则req.body默认为undefined
console.log(req.body);
res.send("ok");
});
app.listen(80, () => {
console.log("启动了127.0.0.1fuwu");
});
4.2 express.urlencoded({ extended: false })使用
在postman中模拟发送 urlencodeed格式的请求
const express = require("express");
const app = express();
//通过express.json()这个中间件,解析表单中的JSON格式数据
//如果不写express.urlencoded({ extended: false })这个中间件 则下面req.body为undefined
app.use(express.urlencoded({ extended: false }));
app.post("/book", (req, res) => {
console.log(req.body);
res.send("ok");
});
app.listen(80, () => {
console.log("启动了127.0.0.1fuwu");
});
第三方中间件
const express = require("express");
const app = express();
//使用
const parser = require("body-parser");
app.use(parser.urlencoded({ extended: false }));
app.post("/book", (req, res) => {
console.log(req.body);
res.send("ok");
});
app.listen(80, () => {
console.log("启动");
});
2.5 自定义中间件
需求描述:自己手动模拟类似 express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据
- 定义中间件
- 监听req和data事件
- 监听req的
end事件
意义:当数据接收完毕后,会自动触发req的end事件,可以在end事件中拿到完整的请求体数据
req.on('end',()=>{
//打印完整的请求提数据
console.log(str)
//TODO:把字符格式的请求体数据,解析成对象格式
})
4.使用querystring模块(node内置模块)解析请求体数据
5.将解析出来的数据对象挂载为req.body
req.on('end',()=>{
const body = qs.parse(str);
req.body = body;//解析后的数据挂载到req.body上
next()
})
6.将定义的中间件封装成模块
未封装完整代码
const express = require("express");
const qs = require("querystring");
//创建 express服务器实例
const app = express();
//这是解析表单的中间件
app.use((req, res, next) => {
//定义中间件的业务逻辑
//1.定义一个str字符串,专门来存储客户端发送过来的请求体数据
let str = "";
//2.监听req的data事件
req.on("data", (chunk) => {
str += chunk;
});
//3.监听req的`end`事件
req.on("end", () => {
//打印完整的请求提数据
//TODO:4.把字符格式的请求体数据,解析成对象格式
const body = qs.parse(str);
//5.解析后的数据挂载到req.body上
req.body = body;
next();
});
});
app.post("/user", (req, res) => {
res.send(req.body);
});
app.listen(80, () => {
console.log("启动 127.0.0.1服务");
});
封装模块后
//建一个 custome-body-parse.js 作为中间件导出模块
const qs = require("querystring");
function bodyParse(req, res, next) {
let str = "";
req.on("data", (chunk) => {
str += chunk;
});
req.on("end", () => {
req.body = qs.parse(str);
next();
});
}
module.exports = bodyParse;
//被执行的.js文件中
const express = require("express");
const qs = require("querystring");
const customeBodyParse = require('./custome-body-parse')
//创建 express服务器实例
const app = express();
//这是解析表单的中间件
app.use(customeBodyParse)
app.post("/user", (req, res) => {
res.send(req.body);
});
app.listen(80, () => {
console.log("启动 127.0.0.1服务");
});
2.6 使用Express写接口
1. 创建基本服务器
const express = require("express");
const app = express();
app.listen(80, () => {
console.log("启动127.0.0.1fuwu");
});
2.创建API路由模块
3.编写Get请求
代码实现:
//1.建一个apiRouter.js文件
const express = require("express");
const apiRouter = express.Router();
apiRouter.get("/get", (req, res) => {
//获取客户端的查询数据
const query = req.query;
res.send({
status: 0,
msg: "GET 请求成功",
data: query, //需要响应给客户段的数据
});
});
module.exports = apiRouter;
2.在执行的app.js文件中调用 router发送get请求
const express = require("express");
const app = express();
const router = require("./apiRouter");
//设置公用路径/api router可以看成一个中间件
app.use("/api", router);
app.listen(80, () => {
console.log("启动127.0.0.1fuwu");
});
4.编写POST请求
代码实现:
//1.在新建的apiRouterPost.js中
const express = require("express");
const router = express.Router();
router.post("/post", (req, res) => {
const body = req.body;
res.send({
status: 1,
message: "这是一个post请求",
data: body,
});
});
module.exports = router;
2.在执行的app.js文件中调用 router发送post请求
const express = require("express");
const app = express();
const router = require("./apiRouterPost");
//配置解析urlencoded数据的中间件
app.use(express.urlencoded({ extended: false }));
//设置公用路径/api router可以看成一个中间件
app.use("/api", router);
app.listen(80, () => {
console.log("启动127.0.0.1fuwu");
});
2.7 CORS跨域资源共享
1.接口跨域问题
const express = require("express");
const app = express();
const router = require("./apiRouterPost");
app.use(express.urlencoded({ extended: false }));
const cors = require('cors')
//一定要在路由之前,配置cors这个中间件,从而解决接口跨域问题
app.use(cors())
app.use("/api", router);
app.listen(80, () => {
console.log("启动127.0.0.1fuwu");
});
2.什么是CORS
3.CORS的注意事项
4.CORS响应头部
- Access-Control-Allow-
Origin
//语法如下
Access-Control-Allow-Origin:<origin> | *
//设置:
res.setHeader('Access-Control-Allow-Origin','http:ltcast.cn')//表示只允许来自http:ltcast.cn的请求
res.setHeader('Access-Control-Allow-Origin','*')//*通配符 表示允许来自任何域的请求
-
Access-Control-Allow-
Headers -
Access-Control-Allow-
Methods5.CORS请求分类(1)简单请求
(2)遇检请求
简单请求和遇检请求的区别:
2.8 JSONP请求
注意:发送Ajax请求时 设置请求的dataType:'jsonp'
1.jsonp接口的注意事项
2.实现JSONP接口的具体代码