项目编译:VSCode
需要用到的安装项:
# Vue 3 项目,安装最新版 Vant:
npm i vant -S
# Vue 2 项目,安装 Vant 2:
npm i vant@latest-v2 -S
npm install --save axios vue-axios
npm i koa --save 安装koa2
npm install --save mongoose 安装mongoose
npm install glob --save 安装glob
npm install --save bcrypt 安装bcrypt进行加密和解密
npm i koa-router --save 安装koa-router
npm install koa-bodyparser --save 安装koa-bodyparser来处理POST请求参数
npm install koa2-cors --save 安装koa2-cors来解决同源策略,解决跨域
项目目录
一、创建项目shopping
用脚手架创建一个项目
vue create shopping
二、在根目录创建serve文件夹并初始化项目
npm init -y
1、在serve文件下创建index.js文件作为程序的主接口
const app = new Koa()
app.use(async(ctx) => {
ctx.body = '<h2>Hello Word</h2>'
})
app.listen(3000, () => {
console.log("koa服务已启动,在3000端口");
})
三、MongoDB的安装、使用
1、windows下的安装
步骤一:
步骤二:选择Custom,自定义安装
选择自己想要的路径安装
步骤三:
"install mongoDB compass" 不勾选,MongoDB Compass 是一个图形界面管理工具
如果在自定义安装在进度条提示到starting service的时候报错:
提示你权限不够,WTF?
这个时候其实MongoDB这个服务已经安装成功。我们不要动这个弹窗,直接打开服务修改如下图,再回来点Retry就可以安装成功了
还有一个问题就是注意事项就是,如果你原来安装过,一定要重启,不然重装会出一些问题。
把mongodb\bin目录设置到环境变量后命令无效问题:
windows10环境变量设置后可能不会生效,一般设置后建议重启电脑。
2、创建数据目录
MongoDB 将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。
请注意,数据目录应该放在根目录下 (如: C:\ 或者 D:\ 等 )。
3、配置环境变量
4、命令行下运行 MongoDB 服务器
为了从命令提示符下运行 MongoDB 服务器,你必须从 MongoDB 目录的 bin 目录中执行 mongod.exe 文件。
5、vue中安装MongoDB for VS Code
四、mongoose的安装使用
1、安装mongoose
npm install --save mongoose
2、启动数据库
在cmd命令行进入C盘目录下的data中的db文件,执行下面的命令
mongod --dbpath C:\data\db
3、连接数据库
我们在项目的serve文件夹下建立一个database文件夹,用来存放和数据库操作有关的文件。
在database文件夹下,建立一个init.js文件,用来作数据库的连接和一些初始化的事情。
/serve/database/init.js
安装glob并引入
3.1安装
npm install glob
3.2 引入glob
const glob = require('glob')
const mongoose = require('mongoose');
const db = 'mongodb://localhost/shopping-db';
//引入glob模块
// glob模块是最简单的模块之一,内容非常少。
//用它可以查找符合特定规则的文件路径名。跟使用windows下的文件搜索差不多
const glob = require('glob');
const { resolve } = require('path');
exports.initSchemas = () => {
glob.sync(resolve(__dirname, './schema', '**/*.js')).forEach(require);
}
exports.connect = () => {
// 数据库的连接
mongoose.connect(db);
let maxconnectNum = 0;
return new Promise((resolve, reject) => {
// 数据库监听操作事件
mongoose.connection.on('disconnected', function() {
console.log('断开连接,并尝试重新连接');
if (maxconnectNum < 4) {
maxconnectNum++;
mongoose.connect(db);
} else {
reject()
console.log('多次尝试依然失败');
throw new Error('数据库异常')
}
})
//数据库出现错误的时候
mongoose.connection.on('error', function() {
console.log('数据库连接发生异常,并重连');
if (maxconnectNum < 4) {
maxconnectNum++;
mongoose.connect(db);
} else {
reject()
console.log('多次尝试依然失败');
throw new Error('数据库异常')
}
})
//连接打开的时候
mongoose.connection.on('open', function() {
console.log('数据库连接成功,connection successful ');
resolve()
})
})
}
然后在serve文件下的index.js里加入立即执行函数,在使用前记得用require进行引入
connect。
//引入connect
const {connect} = require('./database/init.js')
//立即执行函数
;(async () =>{
await connect()
})()
4、定义schema
在database文件夹中新建一个schema文件夹,定义一个user的Schema,命名为User.js
Schema
schema是mongoose里会用到的一种数据模式,可以理解为表结构的定义;每个schema会映射到mongodb中的一个collection,它不具备操作数据库的能力
实际上,schema就是数据库对象的集合,这个集合包含了各种对象如:表、视图、存储过程、索引等。
//引入mongoose模块
const mongoose = require('mongoose');
//声明Schema
const Schema = mongoose.Schema;
// 声明ObjectId
let ObjectId = Schema.Types.ObjectId;
//创建userSchema
//可以声明 schema type 为某一种 type,或者赋值一个含有 type 属性的对象。
const userSchema = new Schema({
UserId: { type: ObjectId },
userName: { unique: true, type: String },//用户名
password: { type: String },//密码
createDate: { type: Date, default: Date.now() },//创建时间
lastLoginTime: { type: Date, default: Date.now() },//最后一次登录时间
},{
//配置后生成的表格中就不加s
collection:'user'
})
定义一个Schema就这么简单,指定字段名和类型
Schema Types内置类型如下:
String
Number
Boolean | Bool
Array
Buffer
Date
ObjectId | Oid
Mixed
5、发布模型model
定义好了Schema,接下就是生成Model。
model是由schema生成的模型,可以对数据库的操作
我们对上面的定义的user的schema生成一个User的model并导出,修改后代码如下:
起一个叫User的,对应userSchema数据
就是添加一句 mongoose.model('User', userSchema)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ObjectId = Schema.Types.ObjectId;
//创建userSchema
const userSchema = new Schema({
UserId: { type: ObjectId },
userName: { unique: true, type: String },//用户名
password: { type: String },//密码
createDate: { type: Date, default: Date.now() },//创建时间
lastLoginTime: { type: Date, default: Date.now() },//最后一次登录时间
})
//发布模型
mongoose.model('User', userSchema)
6、数据库的增删改查
增加
user.save().then((result)=>{
console.log("成功的回调")
},()=>{
console.log("失败的回调")
})
删除
1、删除指定数据
User.remove({userName:"张三"}).then((result)=>{
console.log(result)
})
result:是一个对象 返回值是受影响条数
2、删除所有数据
User.remove({}).then((result)=>{
console.log(result)
})
//删除指定ID
3、User.findByIdAndRemove(id值).then((result)=>{
})
修改
User.update({userName:"张三"},{$set:{userName:"李四"}},{multi:true}).then((result)=>{
console.log(result)
})
multi:true 表示修改多条数据
//更新指定id值的数据
User.findByIdAndUpdate(id值,{$set:{需要修改的内容}}.then((result)=>{
console.log(result)
})
查询
1、查询符合条件的所有数据
User.find({userName:"张三"}).then((result)=>{
console.log(result)
})
result是查到的数据
2、查询所有数据
User.find().then((result)=>{
console.log(result)
})
3、查询单条数据
User.findOne({userName:"张三"}).then((result)=>{
console.log(result);
})
4、条件查询:
$lt(小于) $lte(小于等于) $gt(大于) $gte(大于等于) $ne(不等于);
User.find({"age":{"$lt":20}}).then((result)=>{
console.log(result);
})
User.find({"age":{"$lte":20}}).then((result)=>{
console.log(result);
})
User.find({"age":{"$gt":20}}).then((result)=>{
console.log(result)
})
User.find({"age":{"$gte":20}}).then((result)=>{
console.log(result)
})
User.find({"age":{"$ne":19}}).then((result)=>{
console.log(result)
})
7、加密(哈希和加盐)bcrypt
1、安装
npm install --save bcrypt
2、用pre每次进行保存时都进行加盐加密
let SALT_WORK_FACTOR = 5;
//密码加密
userSchema.pre('save', function(next) {
var myuser = this;
console.log(this);
//产生密码hash当密码有更改的时候(或者是新密码)
// if (!myuser.isModified('password')) return next();
// 产生一个salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// 结合salt产生新的hash
bcrypt.hash(myuser.password, salt, function(err, hash) {
if (err) return next(err);
// 使用hash覆盖明文密码
myuser.password = hash;
next();
});
});
})
3、密码对比comparePwd方法
//密码对比
userSchema.methods = {
comparePwd: (_password, password) => {
return new Promise((resolve, reject) => {
bcrypt.compare(_password, password, (err, isMatch) => {
console.log('对比方法');
console.log(_password);
console.log(password);
console.log(err);
console.log(isMatch);
console.log('对比方法结束');
if (!err) {
resolve(isMatch);
} else {
reject(err)
}
});
})
}
};
五、关联前后端
koa-router
1、安装koa-router
npm i koa-router
2、在serve下新建appApi文件夹,新建user.js用来处理注册登录请求
const Router = require('koa-router');
let router = new Router();
const mongoose = require('mongoose');
//连接数据库初始化
const { connect, initSchemas } = require('../database/init.js');
//立即执行函数
(async() => {
await connect();
initSchemas();
// const User = mongoose.model('User');
// let deleteUser = await User.remove({}).exec();
// console.log('删除成功');
})()
// 注册
router.post('/register', async(ctx) => {
const User = mongoose.model('User');
let newUser = new User(ctx.request.body);
await newUser.save().then(() => {
ctx.body = {
code: 200,
messsage: '注册成功'
}
}).catch(error => {
ctx.body = {
code: 500,
messsage: '服务器异常'
}
})
})
//登录
router.post('/login', async(ctx) => {
//从网页获取到的数据
let loginUser = ctx.request.body;
// 输入框拿到的用户名
let userName = loginUser.userName;
// 输入框拿到的密码
let password = loginUser.password;
console.log(userName);
console.log(password);
//引入User的model
const User = mongoose.model('User');
// 根据用户名去数据库查找相对应的数据
await User.findOne({ userName: userName }).exec().then(async(result) => {
// 如果查询到了,就打印出来整条数据
console.log(result);
// 如果查询到了
if (result) {
//当用户名存在时,开始比对密码
let newUser = new User(); //因为是实例方法,所以要new出对象,才能调用
// 进行密码的解析和对比
// comparePwd是解密的方法
// result.password是数据库中的密码
await newUser.comparePwd(password, result.password).then((isMatch) => {
console.log('对比成功');
//返回比对结果
ctx.body = {
code: 200,
messsage: isMatch
}
}).catch(err => {
console.log(err);
})
} else {
ctx.body = {
code: 201,
messsage: '用户不存在'
}
}
})
})
module.exports = router
3、在index.js下引入koa-router模块
var Router = require('koa-router');
var router = new Router();
app.use(router.routes())
app.use(router.allowedMethods());
koa-bodyparser中间件
利用koa-bodyparser来处理POST请求参数
1、安装
使用npm进行安装,需要注意的是我们这里要用–save,因为它在生产环境中需要使用。
npm install koa-bodyparser
2、引入使用:在index.js引入koa-bodyparser模块
安装完成后,需要在代码中引入并使用。我们在代码顶部用require进行引入。
const bodyParser = require('koa-bodyparser');
3、然后进行使用,如果不使用是没办法调用的,使用代码如下。
app.use(bodyParser());
4、完整写法
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
5、在代码中使用后,直接可以用ctx.request.body进行获取POST请求参数,中间件自动给我们作了解析。
6、更改Register.vue文件和Login.vue文件用来处理post请求(简单的做了一些非空和长度的处理)
serveAPI.config.js中将URL地址进行封装
const LOCALURL = 'http://localhost:3000';
const URL = {
registerUser: LOCALURL + '/user/register',
loginUser: LOCALURL + '/user/login'
}
module.exports = URL
注册界面Register.vue
<template>
<div>
<div>
<van-nav-bar title="用户注册" left-text="返回" left-arrow @click-left="onClickLeft" />
</div>
<div>
<van-cell-group>
<!-- :error-message是密码错误时候的提示 -->
<van-field v-model="userName" required label="用户名" type="text" placeholder="请输入用户名" :error-message="userNameErrorMsg" />
<van-field v-model="password" required label="密码" type="password" placeholder="请输入密码" :error-message="passwordErrorMsg"/>
</van-cell-group>
<!-- loading的作用是防止用户重复操作和用户体验 -->
<van-button type="primary" :loading="openLoading" id="btnRegister" @click="registerMethod" block>马上注册</van-button>
</div>
</div>
</template>
<script>
import url from '@/serveAPI.config.js'
import { Toast } from 'vant';
export default {
data() {
return {
userName: '',
password: '',
openLoading:false,
userNameErrorMsg:'',
passwordErrorMsg:'',
}
},
methods: {
// 非空判断和密码校验
chekform(){
let isOk =true;
if (this.userName==' '|| this.userName.length==0) {
this.userNameErrorMsg="用户名不能为空或空字符";
isOk=false;
} else {
this.userNameErrorMsg='';
}
if (this.password.length<6) {
this.passwordErrorMsg="用户名不能为空或空字符";
isOk=false;
} else {
this.passwordErrorMsg='';
}
return isOk;
},
onClickLeft() {
//用来回退界面
this.$router.go(-1);
},
registerMethod(){
this.chekform && this.register();
},
register() {
if (this.chekform()) {
this.openLoading=true;
this.axios.post(url.registerUser, {
userName: this.userName,
password: this.password
}).then((response) => {
this.openLoading=false;
//Toast轻提示
if (response.data.code==200) {
Toast.success('注册成功');
}else{
Toast.fail('注册失败'+response.data.messsage);
}
console.log(response);
}).catch((error) =>{
this.openLoading=false;
console.log(error);
})
}
}
}
}
</script>
登录界面Login.vue
<template>
<div>
<div>
<van-nav-bar title="用户登录" />
</div>
<div>
<van-cell-group>
<van-field v-model="userName" required label="用户名" placeholder="请输入用户名" />
<van-field v-model="password" required label="密码" placeholder="请输入密码" />
</van-cell-group>
<van-button type="primary" @click="login" block>登录</van-button>
<div class="sign"><a href="/register">注册</a></div>
</div>
</div>
</template>
<script>
import url from '@/serveAPI.config.js'
import { Toast } from 'vant'
export default {
data() {
return {
userName: '',
password: ''
}
},
methods: {
login() {
console.log(url.loginUser);
this.axios.post(url.loginUser, {
userName: this.userName,
password: this.password
}).then((response) => {
if (response.data.code==200) {
Toast.success('登录成功');
} else{
Toast.fail('登陆失败'+response.data.message);
}
console.log(response);
}).catch((error)=>{
console.log(error);
})
},
}
}
</script>
<style scoped>
</style>
koa2-cors中间件解决同源策略,解决跨域
1、安装
npm install koa2-cors --save
2、在serve文件夹下的index.js中写入同源策略解决代码
//引入koa2-cors中间件
const cors = require('koa2-cors');
//注册(使用)中间件
app.use(cors());
主要接口index.js 最终代码
const Koa = require('koa');
const app = new Koa();
const mongoose = require('mongoose');
// const { connect, initSchemas } = require('./database/init.js');
const Router = require('koa-router');
// 处理post请求,把 koa2 上下文的表单数据解析到 ctx.request.body 中
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
// 解决同源策略以及跨域问题用koa2-cors
const cors = require('koa2-cors');
app.use(cors());
let user = require('./appApi/user.js');
// 装载所有子路由
let router = new Router();
router.use('/user', user.routes());
// 加载路由的中间件
app.use(router.routes());
app.use(router.allowedMethods());
// 监听端口号,启动服务
app.listen(3000, () => {
console.log("koa服务已启动,在3000端口");
})
根据自己的想法一步步写出来的,过程中查阅了不少文档,算是比较详细的
作为一个菜鸟,想记录一下自己的学习成长进度,有什么不足的地方,欢迎批评指正