Node.js
一级目录
二级目录
三级目录
文档: http://nodejs.cn
命令
在文件根目录打开 node 命令, 可以运行 js 文件, 按 tab 键自动补全路径; 语法: node 02(按tab)/xx/xx.js
语法 | 作用 |
---|---|
node -v | 查看版本号 |
node | 进入 node |
ctrl+c | 退出 node 需要按两次 |
clear | 清空命令(不等于撤销) |
Node 中的核心模块
path 模块
文档: http://nodejs.cn/api/path.html
path
是 node 本身提供的 API 是专门用来处理路径的模块
-
使用方法
-
先加加载模块
const path = require('path')
-
调用
path
中的方法来处理问题方法 作用 效果 console.log(__dirname) 查看当前文件的绝对路径 E:\xx\当前文件 path.join(‘a’, ‘b’, ‘c’) 智能拼接路径 a/b/c path.join(’__dirname’, ‘b’, ‘c’) 将绝对路径和智能拼接路径结合起来 E:\xx\b\c path.extname(‘index.html’) 获取文件后缀 .html
-
fs 模块
文档: http://nodejs.cn/api/fs.html
fs(文件系统) 是对 文件/文件夹 的操作
-
使用方法
-
先加载模块
const fs = require('fs')
-
调用 fs 模块的方法来处理问题
方法 作用 fs.access(‘path’, callback(err)) 判断路径是否存在 fs.appendFile(file, data, callback) 向文件中追加内容 fs.copyFile(src, callback) 复制文件 fs.mkdir(‘path’, callback) 创建目录 fs.readFile(‘path’, callback(err, data)) 异步读取文件 fs.watchFile(‘path’, ‘内容’, callback(err)) 异步写入文件
-
-
例
fs.readFile('./text.html', 'utf-8', (err, data) => { if(err) { // 读取如果没有发生错误 err = null, 有错误 err = {错误对象} // data 表示读取到的结果 console.log('读取出错是执行的代码') } else { console.log(data) console.log('读取文件成功时执行的代码') } })
querystring 模块
处理查询字符串的模块
-
加载模块
const querystring = require('querystring')
-
调用 querystring 中的模块
方法 作用 效果 querystring.parse(‘id=1&name=zs&age=20’) 将查询字符串解析成 js 对象 { id: ‘1’, name: ‘zs’, age: ‘20’ } querystring.stringify({ id: ‘1’, name: ‘zs’, age: ‘20’ }) 将 js 对象转成查询字符串 id=1&name=zs&age=20
http 模块
可以使用 http 模块来自己搭建 wed 服务器 ( ↓ 是步骤)
-
加载 http 模块
const http = require('http')
-
创建服务对象(服务器), 一般命名为 server
const server = http.createServer()
-
给服务器(server) 对象, 注册请求(request) 事件, 监听浏览器的请求
server.on('request', () => { // 只要有浏览器的请求,就会触发该事件 console.log('我发现你的请求了,但是不想搭理你'); })
-
设置端口,开启服务
server.listen(3000, () => { console.log('服务器启动了'); })
如何对浏览器的请求作出响应
监听浏览器的请求。只要有浏览器的请求,就会触发下面的事件
server.on('request', (req, res) => {
// req:request,请求;它是一个对象;它包含了所有和请求相关的信息
// res:response,响应;它是一个对象;它包含了所有和响应相关的信息
/* res.end('响应的内容'); --- 它可以设置响应体,并做出响应
* res.setHeader(); --- 设置响应头
* res.write('响应体'); -- 设置响应体,但不会做出响应
* res.writeHead() -- 设置响应头,设置响应状态码
* res.statusCode = 404; ---- 设置响应状态码 */
// res.statusCode = 404;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end('hello,我是服务器');
})
注意:
- 必须按顺序使用上述方法,顺序为:
- 设置响应头和状态码(可选res.setHeader()、可选res.setHead()、可选res.statusCode)
- 设置响应体(可选res.write())
- 做出响应(必选res.end(),因为只有调用了res.end方法,才表示做出响应)
- 做出响应(调用res.end方法)之后,不能调用其他方法了。
- 如果使用了res.write()方法,则不能再次设置响应头了
- res.end()方法,只能响应字符串。
根据不同的 url 地址处理不同的请求和 不同接口的处理
上面已经作出响应了, 我们还可以根据不同的 url 和不同的请求方式, 作出不同的响应
-
req 文档: http://nodejs.cn/api/http.html#http_class_http_incomingmessage
- req.url 获取请求行中的路径
- req.method 获取请求行中的请求方法
- req.headers 获取请求头
-
代码
server.on('request', (req, res) => { // 如果浏览器请求的是 /my/userinfo 接口 请求方式是 GET if (req.url === '/my/userinfo' && req.method === 'GET') { /* GET 接口 */ res.end(Date.now()) // 说明浏览器请求的是 /api/login 接口 请求方式是 POST } else if (req.url === '/api/login' && req.method === 'POST') { /* POST 接口*/ // 1. 创建一个空的字符串, 用于保存提交过来的数据 let str = '' // 2. 为 req 注册 data 事件, 只要有数据提交过来就会触发, 当数据较大时是分块接收的 req.on('data', (chunk) => { // chunk -- 块 str += chunk; }) // 3. 为 req 注册 data 事件, 当完全接收了提交过来的数据后就会触发 req.on('end', () => { console.log(str); // id=1&name=zs&age=43 }) } else { // 请求了一个不存在的接口,响应404,表示没有找到 }
MYSQL 数据库
MYSQL 数据库是用来管理存放数据的 用户可以对数据库进行 增删改查可以使用 Navicat 工具来管理数据库
Navicat 的使用
准备工作:
-
启动 MYSQL 服务(打开phpstudy,启动MySQL);
-
打开 Navicat 软件 创建连接:
3.创建数据库
4.创建数据表:
下面是数据表完成后:
id(不允许重复) | name | age | sex | tel |
---|---|---|---|---|
1 | 王宇 | 23 | 男 | 13200008888 |
2 | 王宇 | 24 | 男 | 13300008888 |
3 | 裴志博 | 25 | 男 | 18866669999 |
- id – 自动递增 – √
- 最后保存,填表名
student
。 - 其他补充点
- 数据库中的数字类型
- tinyint -128~127
- smallint -65535 ~ 65535
- int -21亿 ~ 21亿
- bigint 更大
- 数据库中的字符串类型
- varchar - 变长字符串类型
- char - 定长字符串类型
- text - 很长的字符(65535)
- longtext - 21亿个字符
- 数据库中的数字类型
SQL 语句(不区分大小写)
数据查询
--基本的查询语法
select 字段,字段,.... from 表名
--查询所有的字段
select * from 表名
-- 查询id小于10的学生
select * from student where id<10
-- 查询id小于20的女学生
select * from student where id<20 and sex='女'
-- 查询年龄大于等于20小于等于30的学生
select * from student where age>=20 and age<=30
条件查询
--语法:
select * from 表名 where 条件
-- 查询id小于10的学生
select * from student where id<10
-- 查询id小于20的女学生
select * from student where id<20 and sex='女'
-- 查询年龄大于等于20小于等于30的学生
select * from student where age>=20 and age<=30
模糊查询
- %: 代表任意长度(包括0)的任意字符
- _: 代表1位长度的任意字符
-- 语法:
select * from 表名 where 字段 like '%关键字%'
-- 查询名字中带有飞的
select * from student where username like '%飞%'
-- 查询名字的最后一个字是祥的
select * from student where username like '%祥'
统计查询
-- 查询最大的年龄
select max(age) from student
-- 查询最小的年龄
select min(age) from student
-- 查询年龄的平均值
select avg(age) from student
-- 查询年龄的总和(所有人的年龄加到一起)
select sum(age) from student
-- 重点:查询总记录数(一共有多少条数据)
select count(*) from student
-- 可以为查询到的字段定义别名
select count(*) 别名 from student
查询结果排序
-- 语法:
select * from 表名 order by 字段 asc(升序)/desc(降序)
-- 查询所有的学生,按年龄降序排序
select * from student order by age desc
-- 查询所有的男同学,并按年龄降序排序
select * from student where sex='男' order by age desc
注意:如果SQL语句中,有where和order by,where一定要放到order by之前
限制查询结果
-- 语法:
select * from 表名 limit 起始位置, 长度
-- 查询所有学生中的第1、2、3个
select * from student limit 0, 3
注意:where、order by、limit如果一起使用,是有顺序的,where在最前面、其次是order by、limit要放到最后。另外三者之间没有and之类的
添加数据
- 指定字段和值,只要字段和值对应即可。和顺序无关
-- 语法:
insert into student (字段, 字段, ...) values (值, 值, ...)
insert into student (username, age, sex) values ('虚空恐惧', 98, '男')
- 和顺序有关,因为没指定字段,所以值必须是所有的值,而且顺序和表中字段的顺序要一致
insert into heroes values (null, '拉克丝', '光辉女郎', '女', 28)
- 使用set里设置新数据的值,没有顺序关系
-- insert into 表名 set 字段=值, 字段=值, ....
insert into student set age=30, sex='男', username='李青'
修改(更新)数据
-- 语法
update 表名 set 字段1=值1, 字段2=值2,... where 修改条件
update student set age=20, sex='女' where id=11
不指定条件会修改所有的数据
删除数据
-- 语法
delete from 表名 where 删除条件
delete from student where id=11
不指定条件将删除所有的数据
npm 基础使用
npm
是 node
包(模块)管理工具, 安装 node
时自动安装的.
作用: 1. 下载并安装第三方的模块
2. 卸载第三方模块
3. 发布模块
4. 删除已发布的模块
本地模块 (第三方)
本地模块: 只能在 当前文件夹和当前文件夹的子文件夹中 使用
下载安装第三方模块
npm install 模块名
# 简写
npm i 模块名
卸载模块
npm uninstall 模块名
# 简写
npm un 模块名
演示
// 加载模块
const path = require('path')
// 调用模块的方法完成功能
path.join()
全局模块
全局安装的模块, 不能通过 require()
加载使用, 一般都是命令或者工具
全局安装的模块, 安装在 c 盘, 通过npm root -g
可以查看全局安装路径
下载安装模块(多了一个 -g
)
npm i 模块名 -g
# 或
npm i -g 模块名
# mac 系统如果安装不上,使用下面的命令提高权限
sudo npm i -g 模块名
卸载命令(多一个 -g
)
npm un 模块名 -g
##全局安装 nodemon 模块
安装命令
npm i nodemon -g
作用: 代替 node 启动服务器, 当执行 nodmon 文件名
之后会自动重启服务
如果报错如下:
解决办法是:
管理员
方式,打开命令行窗口
执行 set-ExecutionPolicy RemoteSigned;
在出现的选项中,输入 A
,回车。即可
mysql 模块
mysql
是一个第三方模块, 专门用来操作 MYSQL 数据库. 可以进行增删改查
想要使用 mysql
模块 首先需要安装
# 如果之前没有安装过其他模块, 就需要先初始化
npm init -y
# 下载模块
npm i myspl
mysql使用步骤
一共需要五个步骤
// 1. 加载 mysql 模块
const mysql = require('mysql')
// 2. 创建连接对象(连接数据库) 设置连接参数(填写账号密码)
const conn = mysql.createConnection({
// 属性: 值
host: 'localhost',
port: 3306, // navict 和数据库之间连接的端口, 代码操作时也可以不写?
user: 'root',
password: '密码',
database: '(数据)库名'
})
// 3. 连接到 mysql 服务器
conn.connect()
// 4. 完成查询 (增删改查)
// sql 语句= select * from student limit ?
conn.query(SQL语句, [SQL 中占位符的值], (err, result, fields) => {
// err: 错误信息
// result: 查询结果
// fields: 当前查询过程中涉及到的字段信息,一般用不着
})
// 5. 关闭连接, 释放资源
conn.end()
result的 主要属性以及返回的数据类型 如下:
- 查询语句
result
– 数组- 数组的每个单元,就是查询到的一行数据
- 添加语句
result
– 对象- result.affectedRows – 受影响的行数
- result.insertId – 新增数据id
- 修改语句
result
– 对象- result.affectedRows – 受影响的行数(满足条件的)
- result.changedRows – 被改变的行数
- 删除语句
result
– 对象- result.affectedRows – 受影响的行数
封装 MYSQL
使用 mysql 一般都需要封装(用形参传值)用的时候直接调用函数就好
function abc(sql, params, callback) {
const mysql = require('mysql');
const conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '12345678',
database: 'hahaha'
});
conn.connect();
// 完成增删改查
conn.query(sql, params, callback);
conn.end();
}
// 导出(一定要导出否则此工具被当做模块加载后无法使用)
module.exports = abc;
Express
Express: 是一个基于 node 的第三方模块, 用于快速搭建服务器
注: 在使用 express 的时候仍然可以使用 http 模块的方法
文档: 官网: http://expressjs.com
中文文档(非官方): http://www.expressjs.com.cn
腾讯云开发者手册: https://cloud.tencent.com/developer/doc/1079
构造服务器的步骤
// 使用express 搭建web服务器
// 1) 加载 express 模块
const express = require('express')
// 2) 创建 express 服务器
const app = express()
// 3) 开启服务器
app.listen(3006, () => console.log('express服务器开始工作了'))
// 4) 加载封装的 mysql 工具
const abc = require('abc')
// 4) 监听浏览器请求并进行处理
app.get('GET请求的地址', (req, res) => {
abc.('sql 语句', [传值], (err, result) => {
// 查询失败返回 err
if(err) throw err
// 查询成功将 设置的内容和结果 响应给客户端
res.send({
status: 0,
message: 'xx',
data: result
})
})
})
app.post('POST请求的地址', 处理函数)
res.send() 方法可以替换 res.end() 对服务器做出响应
使用 res.send() 应该注意:
- 响应的内容不能为数字
- 如果响应的是JS对象,那么方法内部会自动将对象转成JSON格式。
- 如果已经做出响应了,就不要再次做出响应了。
中间件
中间件原理:
中间件函数的几种用法
// 下面的中间件,只为当前接口 /my/userinfo 这个接口服务
app.get('/my/userinfo', 中间件函数);
app.get('/api/login', 中间件函数, 中间件函数, 中间件函数, 中间件函数 .....);
// 下面的几个中间件,是处理 /api/login 接口的
app.post('/api/login', 中间件函数)
app.post('/api/login', 中间件函数, 中间件函数, 中间件函数, 中间件函数 .....);
// app.use 中的中间件,可以处理所有的GET请求和所有的POST请求,没有指定路径,那么处理所有接口
app.use(中间件函数);
app.use(中间件函数, 中间件函数, 中间件函数 .....);
// 下面的中间件函数,只处理 /api 开头的接口
app.use('/api', 中间件函数);
// 下面的中间件函数,处理 /abcd 、 /abd 这两个接口
// ? 代表 c 可以有也可以没有
app.get('/abc?d', 中间件函数);
app.post('/abc?d', 中间件函数);
app.use('/abc?d', 中间件函数);
中间件分类:
- 应用级别的中间件
- 路由级别的中间件
- 错误处理中间件
- 内置中间件(express自带的,比如 express.urlencoded())
- 第三方中间件(比如multer、express-jwt、express-session、…)
中间件的 语法和特点
特点:
- 在 js 代码中, 所有的 req和res 是一个对象. 所有的中间件函数 共享
- 在中间件函数中不调用
next()
, 程序执行到当前中间件函数后, 不在向后执行, 为了防止代码逻辑混乱, 调用next()
后不要再写额外的代码 - 有可能因为中间件函数顺序的原因, 某一个中间件函数不会执行
- 使用
app.use()
注册的中间件, GET,POST 请求都可以触发 - 错误处理中间件, 必须传递 err, req, res, next 四个参数, 而且要放到所有接口的后面.
- 如果前面的中间件没有给 next 传参, 并且代码也没有错误, 请求就不会进入到错误处理中间件
- 如果前面的中间件, 为 next 传递了实参, 程序会绕过后面所有的中间件, 直接进入到最后的错误处理中间件
语法:
- 中间件函数有四个基本的参数, err, req, res, next
- 如果带有 err 参数, 并且函数处理中间件有四个参数, 那么这个表示 错误处理中间件
- req 就是请求相关的对象
- res 就是响应相关的对象
- next 他是一个函数 在不使用的时候可以省略(除了错误处理中间件)
- 班写好的中间函数,传递给
app.get()
、app.post()
、或app.use()
使用
// 第一个中间件
app.use((req, res, next) => {
req.aa = '123';
next();
})
// 第二个中间件(只处理以 /api 开头的接口)
app.use('/api', (req, res, next) => {
req.bb = '456';
next();
})
// 第三个中间件(只处理以 /my 开头的接口)
app.use('/my', (req, res, next) => {
req.cc = '789';
next();
})
// 下面接口中的三个中间件,只能 /api/test 这个请求
app.get('/api/test', (req, res, next)=>{
next();
}, (req, res, next)=>{
next();
}, (req, res) => {
console.log(req.aa, req.bb, req.cc)
res.send('hello');
})
// 错误处理中间件
app.use((err, req, res ,next) => {
})
utility (第三方模块)
作用: 在实际开发中, 不允许数据表中存在明文密码所以要对密码进行 MD5
加密,
修改代码,将密码进行加密处理,然后 再添加到数据库:
+ const utility = require('utility');
router.post('/reguser', (req, res) => {
// 接收账号和密码
// console.log(req.body); // { username: 'laotang', password: '123456' }
+ req.body.password = utils.md5(req.body.password);
db('insert into user set ?', req.body, (err, result) => {
if (err) {
console.log(err.message)
res.send({ status: 1, message: err.message });
} else {
res.send({ status: 0, message: '注册成功' })
}
})
});
jsonwebtoken(生成 token)
文档: https://github.com/auth0/node-jsonwebtoken
语法:
let token = jwt.sign(数据载体, 秘钥, [配置项, 回调函数]);
- 数据载体:必须写成对象形式,用于保存用户信息
- 秘钥:字符串类型,随便填就行。后续解密token的时候,还需要用到它
- 配置项:必须是对象形式,expiresIn表示有效期;
- 回调函数:函数类型,token生成之后触发的一个函数
使用方法:
+ const jwt = require('jsonwebtoken');
router.post('/login', (req, res) => {
// 客户端提交账号密码过来
// 1. 接收账号密码 (req.body)
let username = req.body.username;
let password = utils.md5(req.body.password);
// 2. 判断账号和密码是否正确
db('select * from user where username=? and password=?', [username, password], (err, result) => {
if (err) {
res.send({ status: 1, message: err.message });
} else {
// console.log(result);
if (result.length > 0) {
+ // token前面必须加 Bearer空格,一定要注意空格
+ let token = 'Bearer ' + jwt.sign({ userId:result[0].id}, 'shhhhh', { expiresIn: '2h'})
- res.send({ status: 0, message: '登录成功' })
+ res.send({ status: 0, message: '登录成功', token:token })
} else {
res.send({ status: 1, message: '账号或密码错误' });
}
}
});
});
express-jwt(解密 token)
expresss-jwt 是一个第三方模块
文档: https://github.com/auth0/express-jwt
注意:
- 需要配合
express
使用的第三方中间件 - 代码中
expressJWT()
函数的作用是解密 token 字符串, 将 token 中保存的用户信息, 赋值给req.user
,secret
的值需要和生成 token 是时填写的秘钥一样, 否则将解密失败 - 在代码中
unless()
方法的作用是控制那些接口不需要认证
代码:
// 搭建服务器的三行代码
const express = require('express');
const app = express();
app.listen(3006, () => console.log('启动了'));
+ // 加载 express-jwt 模块
+ const expressJWT = require('express-jwt')
+ // 解密 token
+ app.use(expressJWT({
+ secret: 'shhhhh', // 解密字符串,和加密时的秘钥一样
+ algorithms: ['HS256']
+ }).unless({
+ // 除了登录和注册的接口之外,其他接口都需要认证
+ path: ['/api/login', '/api/reguser']
+ }));
// 配置一下,使用express提供的方法来接收客户端提交的查询字符串;并且把接收到的结果赋值给 req.body
app.use(express.urlencoded({ extended: false }));
// 导入 login 路由对象
app.use('/api', require('./routers/login'));
// 导入category路由对象,注册成中间件
app.use('/my/article', require('./routers/category'));
+ // 错误处理中间件, 如果 token 解密失败时会触发
+ app.use(function (err, req, res, next) {
+ if (err.name === 'UnauthorizedError') {
+ res.status(401).send({ status: 1, message: '身份认证失败!' });
+ }
+ });
修改镜像源
镜像网站就是复制的跟别人的网站一模一样(资源), 因为我们安装完 node , 都是从 npm 的主站(主站在海外)上面下载第三方模块,速度较慢
nrm 模块(控制镜像源)
- nrm 是一个全局的模块 使用前需要安装 (npm i nrm -g)
nrm ls
查看的=可用的镜像源nrm use taobaO
切换镜像源(国内一般使用淘宝)
- 如果 nrm 安装不上
- 直接使用命令修改镜像源为淘宝
npm config set registry https://registry.npm.taobao.org
- 淘宝会间隔十分钟更新一次 npm 主站上的模块
package.json 文件
- 执行
npm init -y
初始化之后, 会自动生成这个文件- 文件夹不能是中文的, 否则会初始化失败
- 文件夹名, 不要和我们准备安装的模块重名, 比如 mysql, experess
dependencies
翻译过来是依赖的意思。- 每次安装第三方模块都会在这里记录
- 如果你的
node_modules
文件夹删除了, 直接执行npm i
可以把依赖中记录的模块全部安装
ES6 降级处理
因为 ES6 有六蓝旗件如问题, 可以使用一些工具进行降级处理 如:babel
使用步骤
-
项目初始化
npm init -y
-
在命行中, 安装 babel 官网连接: https://babeljs.io
npm install @babel/core @babel/cli @babel/preset-env
-
配置文件
.babelrc
(这个文件需要手工创建)// babel 的降级处理配置 { "presets": ["@babel/preset-env"] }
-
在命令中, 运行
// 把转换的结果输出到指定的文件(如果没有系统自动创建) npx babel index.js(原文件) -o indes1.js(转换后的文件) // 把转换的结果输出到指定的目录 npx babel 包含有 js 的目录 -d 转换后的目录