一、NodeJS
1、 全局变量
-
global:全局变量的宿主(类似于浏览器js中的window对象),这是一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问
-
__filename:当前正在执行的脚本的文件名(完整/绝对路径)
-
__dirname:当前执行脚本所在的目录路径(目录的绝对路径)
console.log(global); console.log(__filename); console.log(__dirname);
2、常用内置模块
更多内置模块及常用内置模块的更多API用法,可以参考:首页 | Node.js v20.3.1 文档
2.1、os模块
os(operation system)模块提供了与操作系统相关的实用方法和属性。
const os = require('os') // 换行符 os.EOL //根据操作系统生成对应的换行符 window \r\n,linux下面 \n
// cpu相关信息 os.cpus()
// 总内存大小 (单位 字节) os.totalmem()
// 空余内存大小 (单位 字节) os.freemem()
// 主机名 os.hostname()
// 系统类型 os.type()
2.2、path模块
path模块用于处理文件和目录(文件夹)的路径。
const path = require('path') // 获取路径最后一部内容,一般用它来获取文件名称
path.basename('c:/a/b/c/d.html') // d.html // 获取目录名,路径最后分隔符部分被忽略
path.dirname('c:/a/b/c/d.html') // c:/a/b/c // 获取路径中文件扩展名(后缀)
path.extname('c:/a/b/c/d.html') // .html // 给定的路径连接在一起
path.join('/a', 'b', 'c') // /a/b/c // resolve:模拟cd(切换目录)操作同时拼接路径
2.3、url模块
URL字符串是结构化的字符串,包含多个含义不同的组成部分。 解析字符串后返回的 URL 对象,每个属性对应字符串的各个组成部分。
const url = require('url');
const href = 'http://www.xxx.com:8080/pathname?id=100#bbb' // 解析网址,返回Url对象 // 参2 如果为true 则 query获取得到的为对象形式
url.parse(href,true) //以一种 Web 浏览器解析超链接的方式把一个目标 URL 解析成相对于一个基础 URL。
2.4、querystring模块
用于解析和格式化 URL 查询字符串(URL地址的get形式传参)的实用工具。
const querystring = require('querystring') // query字符串转为对象
querystring.parse('foo=bar&abc=xyz')
querystring.decode('foo=bar&abc=xyz') // 对象转为query字符串
querystring.stringify({ foo: 'bar',abc: 'xyz'})
querystring.encode({ foo: 'bar',abc: 'xyz'})
2.5、fs模块
fs(file system)模块提供了用于与文件进行交互相关方法。
注意:fs模块提供了2大类api方法
同步操作
异步操作
const fs = require('fs') // 写入数据(覆盖),追加写使用fs.appendFile
fs.writeFile(文件路径,待写入的数据,err => {}) // 读取文件中数据
fs.readFile(文件路径, 'utf8’,(err,data) => {}) // (同步)检查文件是否存在 返回true/false // async:异步 // sync:同步
let ret = fs.existsSync(path) // 获取文件信息(异步)
fs.stat(文件,(err,stats) => {
stats.isDirectory() // 是否是目录
stats.isFile() // 是否为文件
stats.size // 文件大小(以字节为单位)
})
// 删除文件(异步)
fs.unlink(文件路径,err => {})
// 友情提醒:fs模块有点小坑 // 关于相对路径,在fs模块中,读写文件都会使用文件的路径,如果是相对路径则相对路径相对于谁? // 答案:不会是相对于当前的js文件,而是相对于node命令执行的位置(即命令行的工作路径)。以后再用fs的时候建议文件的路径采用__dirname与文件名做拼接的方式来写文件名。
2.6、http模块
需要了解web服务器的相关内容。
2.6.1、创建web服务器
NodeJs是通过官方提供的http模块来创建 web服务器的模块。通过几行简单的代码,就能轻松的手写一个web服务,从而对外提供 web 服务。
// 导入http模块
const http = require('http')
// 创建web服务对象实例
const server = http.createServer()
// 绑定监听客户端请求事件request
server.on('request', (request, response) => {})
// request: 接受客户端请求对象,它包含了与客户端相关的数据和属性
request.url 客户端请求的uri地址
request.method 客户端请求的方式 get或post
request.headers 客户端请求头信息(对象)
// response:服务器对客户端的响应对象
设置响应头信息 ,用于响应时有中文时乱码解决处理
response.setHeader('content-type', 'text/html;charset=utf-8')
设置状态码
response.statusCode = 200
向客户端发送响应数据,并结束本次请求的处理过程
response.end('hello world')
// 启动服务
server.listen(8080, () => { console.log('服务已启动') })
案例:手写一个服务器软件,启动后要求用户访问根“/”输出hello world,用户访问/html5输出2021。
// 1. 导入http模块 const http = require("http"); // 2. 创建web服务实例 const server = http.createServer(); // 3. 监听request请求 server.on("request", (req, res) => { // 输出hello world // res.end("hello world"); if (req.url === "/") { res.end("hello world"); } if (req.url === "/html5") { res.end("2021"); } }); // 4. 启动服务 server.listen(8080, () => { // 仅是提示作用,可以不写,但是建议写 console.log("server is running at http://127.0.0.1:8080"); });
2.6.2、静态资源服务器
静态资源:常见的有html、css、js、图片、音频、视频等。特征:其展示的效果要想变化必须要更改文件的内容。
静态资源服务器:专门保存上述静态资源的服务器,称之为静态资源服务器。
-
实现思路
客户端请求的每个资源uri地址,作为在本机服务器指定目录中的文件。通过相关模块进行读取文件中数据进行响应给客户端,从而实现静态服务器。
2.6.3、get数据获取
get数据通过地址栏使用query方式进行传递的数据 例?id=1&name=zhangsan
// 导入
const http = require('http');
const url = require('url');
// 创建实例&监听request事件&监听端口
http.createServer((req, res) => {
// 之前第3步中的回调函数 // 获取地址栏中 query数据
let { query } = url.parse(req.url, true);
console.log(query);
}).listen(8080)
2.6.4、post数据获取
表单数据多数为post进行提交到服务器端。需要监听req对象的data事件来获取客户端发送到服务器的数据。如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后分批次发送给服务器。所以data事件可能会被触发多次,每次触发data事件时,收到的数据只是全部数据的一部分,因此需要做数据的拼接才能得到完整的数据:
const http = require('http');
const queryString = require('querystring');
http.createServer((req, res) => {
let arr = [];
// 数据接受中
req.on('data', buffer => {
arr.push(buffer);
});
// 数据传输结束了
req.on('end', () => {
// 拼接接受到的所有数据
let buffer = Buffer.concat(arr);
let post = queryString.parse(buffer.toString())
console.log(post);
});
}).listen(8080)
二、 Express
网址:Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网
1、创建web服务
- 导入需要使用的express包
- 创建web实例
- 定义允许访问的地址(定义路由)【重复性步骤】
- 原先的输出:res.end()
- 现在的输出:res.send()
-
启动服务(监听端口)
const express = require('express')
// 创建web服务
const app = express()
// 监听 get请求
// req 请求对象
// res 响应对象
app.get('请求URI',(req,res)=>{
// 向客户端响应数据
res.send({id:1,name:'张三'})
})
// 监听POST请求
app.post('请求URI',(req,res)=>{})
// 其他app.形式的api方法,put/delete/use 等
app.put()
app.delete()
// ....
// 启动服务
app.listen(8080,()=>{})
1.1、获取query字符串
获取get传值的参数。
通过 req.query 对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数:
app.get('/',(req,res)=>{
console.log(req.query)
// 获取到的直接就是个对象
})
1.2、动态参数传递
默认情况下,express是不支持使用动态参数的,必须要声明路由后才支持。但是“?”号传参不需要声明。
Express也支持类似于Vue中动态路由的形式传递参数,传递的参数通过 req.params 对象可以访问到:
// 必须的路由参数(不传就匹配不上,返回404错误)
app.get('/:id',(req,res)=>{
console.log(req.params.id)
})
// 可选的路由参数(传递与否都不会报错)
app.get('/:id?',(req,res)=>{
console.log(req.params.id)
})
这个传参方式是符合restful传参规范的。
扩展Restful规范(规范不是标准!!!):
规范1:restful规范是一个接口开发的规范(一般用于后端,但前端也可以使用)
规范2:restful规范规定了多种请求类型来适配不同的操作,常见的如下:
GET请求类型:用于获取数据(获取xxx列表、获取xxx详情)
POST请求类型:用于数据新增(xxx添加)
PUT请求类型:用于数据修改(xxx修改、xxx编辑)
DELETE请求类型:用于数据删除(xxx删除)
1.3、静态资源托管
express提供了一个非常好用的方法(内置中间件),叫做 express.static(),通过此方法,可以非常方便地创建一个静态web资源服务器:
app.use(express.static('public'))
// app.use()表示使用(中间件),现在可以访问public目录下所有的文件
// 如public/aa.jpg文件,则可以通过 : http://xxxx/images/aa.jpg
express还支持给静态资源文件创建一个虚拟的文件前缀(实际上文件系统中并不存在),可以使用 express.static 函数指定一个虚拟的静态目录,就像下面这样:
app.use('/static', express.static('public'))
现在你可以使用 /static 作为前缀来加载 public 文件夹下的文件了:
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/hello.html
前缀前面的“/”必须要加,否则【404】
使用app.use()方法一般写在具体的路由监听之前。
前缀的使用意义:
可以迷惑别人,一定程度上阻止别人猜测我们服务器的目录结构
可以帮助我们更好的组织和管理静态资源
2、路由&&模块化
express为了路由的模块化管理功能,通过express.Router()方法创建路由模块化处理程序,可以将不同业务需求分开到不同的模块中,从而便于代码的维护和项目扩展。
路由模块化处理可以分为以下步骤来完成
a. 创建路由的主文件src/index.js
const express = require("express"); const app = express();
// 导入路由模块
const usersRouter = require("./router/users.js");
const goodsRouter = require("./router/goods.js");
// 使用路由路由
// app.use(usersRouter);
// app.use(goodsRouter);
// 路由也支持类似于静态资源托管的操作,设置特定的前缀
// 将公共的"/admin"提取出来
// 路由前缀:app.use(前缀,路由模块)
app.use("/admin", usersRouter);
app.use("/admin", goodsRouter);
app.listen(3000, () => { console.log("server is running at http://127.0.0.1:3000"); });
b. 创建users模块路由文件src/router/users.js
// 获取router对象
const express = require("express");
const router = express.Router();
// 编写路由规则
// 用户列表
// 路由地址的第一个斜杠是不能去除的 r
outer.get("/users", (req, res) => { res.send("你访问的是用户列表"); });
// 用户详情
// 路由地址的第一个斜杠是不能去除的
router.get("/users/:uid", (req, res) => { res.send("你访问的是用户详情,传递的id是" + req.params.uid); });
// 导出
module.exports = router;
c. 创建goods模块的路由文件src/router/goods.js
const express = require("express"); const router = express.Router();
// 商品添加
router.post("/goods", (req, res) => { res.send("你访问的是商品添加"); });
// 商品修改
router.put("/goods/:gid", (req, res) => { res.send("你访问的是商品修改,传递的id是" + req.params.gid); });
// 商品删除
router.delete("/goods/:gid", (req, res) => { res.send("你访问的是商品删除,传递的id是" + req.params.gid); });
// 导出
module.exports = router;
2、中间件
2.1、内置中间件
2.1.1 express.static :
参考1、3
app.use('前缀',express.static('托管目录地址'))
2.1.2 express.json:
接收json格式提交的数据,其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上
app.post("/", express.json(),(req, res) => { console.log(req.body); })
2.1.3 express.static:
处理post表单数据,其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上
// 局部中间件,处理表单提交的数据
app.post("/post", express.urlencoded({ extended: false }), (req, res) => {
// 输出得到的请求体
console.log(req.body);
});
注意:关于urlencoded中间件中的配置项extended值的说明
-
值默认为true,但是不建议使用默认的true
-
值true与false的区别
-
false:使用querystring库去解析post数据
-
去除获取到的数据对象的内置方法
-
接收到的数据只有字符串与数组的形式
-
-
true:使用qs库去解析post数据
-
使得获取到的数据对象更加面向对象化
-
-
2.2、自定义中间件
2.3、第三方中间件
2.4、错误类型中间件
三、sequelize
官网:Sequelize 简介 | Sequelize中文文档 | Sequelize中文网
Sequelize是一个基于 Node.js 的 ORM,用于在 JavaScript 中对 SQL 数据库进行一些操作。以下将简要介绍一些主要的函数和相应的用法。
1. 创建模型
使用Sequelize.define('name', attributes, options) 可以定义一个模型。
例如:
const { STRING, INTEGER, BOOLEAN, JSON, DATE } = sequelize
const User = sequelize.define('user', {
username: {
type: STRING,
field: 'user_name',
},
birthday: DATE
},
{
tableName: 'base_api',
timestamps: true,
timezone: '+08:00',
primaryKey: 'username'
}
);
以上代码即定义了一个名为‘user’的模型,并设置了username和birthday两个字段属性。
第二个参数是一个选项对象,用于指定模型的一些配置。其中:
-
tableName:指定模型对应的数据库表名。在这个例子中,模型对应的表名是 base_api。
-
timestamps:指定是否在模型中自动添加 createdAt 和 updatedAt 字段。如果设置为 true,Sequelize 会自动在每条记录中添加这两个字段,用于记录创建时间和更新时间。
-
timezone:指定时区。如果你的数据库使用的是 UTC 时间,可以使用这个选项来指定时区。
-
primaryKey: 指定主键
2. 同步数据库
可以使用 .sync() 方法来同步我们定义的模型到数据库中。
sequelize.sync().then(() => {});
这会创建一个与我们模型相匹配的表。如果已经存在,则不会重新创建。
也可以使用 sync({ force: true }) 来强制创建表。
sequelize.sync({force: true}).then(() => {});
这会删除先前存在的用户表,并新建一个空的用户表。
3. 插入数据
使用.create()方法:
User.create({username: 'John', birthday: new Date()})
.then(user => {})
.catch(err => {});
4. 查找数据
可以用.findAll() 或 .findOne() 查找数据:
User.findAll({where: {username: 'John'}})
.then(users => {})
.catch(err => {});
5. 主键搜索
可以用.findById() 或 .findByPk() 查找数据:
User.findByPk(1)
.then(user => {})
.catch(err => {});
这个示例会查询主键为 1 的用户记录,并输出它的名字。如果没有查询到记录,则输出一个错误信息。
5.1 主键
主键(Primary Key)是数据库表中用于唯一标识每一条记录的字段或字段组合。主键的作用是保证每一条记录都有一个唯一的标识,方便对记录进行操作和管理。
在关系型数据库中,每个表都必须有一个主键。主键可以由一个或多个字段组成,但是必须满足以下条件:
- 主键的值必须唯一,不能重复。
- 主键的值不能为空,不能为 NULL。
- 主键的值不能被修改,一旦确定就不能更改。
通常情况下,主键是由数据库自动生成的,比如自增长的整数或全局唯一的字符串。
在 Sequelize 中,每个模型都必须有一个主键。如果你没有指定主键,Sequelize 会自动为你创建一个名为 `id` 的整数类型主键。如果你需要指定其他的主键,可以在模型定义中使用 `primaryKey` 属性来指定。例如:
const User = sequelize.define('user', {
username: DataTypes.STRING,
email: DataTypes.STRING
}, {
primaryKey: 'email'
});
这个示例中,我们将 `email` 字段指定为主键。这样,每个用户记录都会有一个唯一的邮箱地址作为主键。
6. 更新数据
6.1、可以用.update()方法:
User.update({username: 'Jack'}, {where: {username: 'John'}})
.then(result => {})
.catch(err => {});
以上代码即把名字为 "John" 的用户修改为名字为 "Jack"。
6.2、可以用.bulkUpdate()方法:
User.bulkUpdate({username: 'Jack'}, {where: {username: ['John', 'Tom']}})
.then(result => {})
.catch(err => {});
以上代码即把名字为 "John" 和 "Tom"的用户修改为名字为 "Jack"。
7. 删除数据
7.1、可以用.destroy()方法:
User.destroy({where: {username: 'John'}})
.then(result => {})
.catch(err => {});
以上代码即删除名字为 "John" 的记录。
7.2、可以用.bulkDestroy()方法:
User.destroy({where: {username: ['John', 'Tom']}})
.then(result => {})
.catch(err => {});
以上代码即删除名字为 "John"和 'Tom' 的记录。
8.统计记录数
可以用.count()
const count = await User.count({
where: {
age: {
[Op.gte]: 18
}
}
})
where 属性用于指定查询条件,[Op.gte] 表示大于等于的运算符,即年龄大于等于 18 岁。执行 count 方法后,将返回符合条件的记录数
9.插入或更新
使用 upsert()
const [user, created] = await User.upsert({
name: '张三',
age: 20
}, {
where: {
name: '张三'
}
})
console.log(user) // 输出插入或更新的记录
console.log(created) // 输出是否插入了新记录const [user, created] = await User.upsert({
name: '张三',
age: 20
}, {
where: {
name: '张三'
}
})
where 属性用于指定查询条件,如果数据库中已经存在 name 为 '张三' 的记录,则更新该记录的 age 字段为 20,否则插入一条新记录。
执行 upsert 方法后,将返回插入或更新的记录和一个布尔值,表示是否插入了新记录