闲来无事,试了一下 Koa,第一次搞感受还不错,这个项目比较基础但仍是比较完整了,仍是有必定的参考价值css
如下是项目地址,但愿给个 star
,鼓励一下:前端
前端 gitHub
地址vue
后端 gitHub
地址node
PS: 数据库我放在了后端项目的 db-daike
目录下ios
项目名:《代课》git
介绍:大学期间加入了两个比较大的社团,虽然已经毕业多年(这个夏天恰好一年哈哈),社团的群里有不少学弟学妹常常发一些帮忙代课的信息,而且也会附带一些好处等等...都是这么过来的,确实比较了解有的课程老师就爱点名,三次就挂科(我没有说《毛概》),好吧扯远了!大概需求就是这样的...我写这个项目的缘由就是为了实现这样一个目的...github
效果预览:web
这个就是咱们最终作出来的样子,那么开始干正事以前咱们仍是先捋一捋技术栈吧,说一说都用到了那些东西mongodb
主要仍是分为三大块:前端(Vue) + 后端(NodeJS - Koa)+ 数据库(MongoDB)vuex
UI 框架随便选了一个,对!就是这么随意: Vant
css采用 scss
登陆页的logo也是用的一个网站在线制做的,很差意思,网址我给忘了
项目结构(大体)以下:
├── axios // 对 axios进行 二次封装
│ └── interface // api 文件目录
├── src
│ ├── router // 路由配置
│ └── views // 路由页面
└── vuex // 全局的状态
└── views // 按路由模块进行状态分组
复制代码
Tip: 结构中不少部分我省略了,一部分是属于 vue
全家桶的就不必赘述了,另外一部分好比请求接口和 Vuex
的模块文件,以后会有讲到
后端采用 NodeJS
,框架采用的是 Koa
其它就是在项目中使用的第三方库,这里理列一下吧:
// ...
// 在项目中使用的文件中,我都有写这些库的npm或git地址方便学习
{
"dependencies": {
// 加密用户密码(数据库没有存明文密码)
"bcrypt": "^3.0.0",
// 解析前段请求参数
"koa-bodyparser": "^4.2.1",
// 路由
"koa-router": "^7.4.0",
// 解决跨域
"koa2-cors": "^2.0.6",
// 操做 mongoDB 数据库
"mongoose": "^5.2.7",
// 生成惟一 id
"uuid": "^3.3.2"
}
}
// ...
复制代码
后端的项目结构有必要说一些,这个是我参考一些比较规范的项目本身搞的,也是比较随意了,哈哈(我也是第一次这么搞):
├── app // 对 axios进行 二次封装
│ └── controllers // 控制器文件目录,用来操做数据库
│ │ └── ... // 对应操做的表,这里就省略了
│ ├── middleware// 自定义中间件目录
│ ├── models // 定义的表结构
│ │ └── ... // 对应的表,这里就省略了
│ └── utils // 工具模块目录
│ │ └── ... // 工具模块,这里就省略了
├── rotes // 路由文件
│ ├── router // 路由配置
│ └── views // 路由页面
└── vuex // 全局的状态
└── views // 按路由模块进行状态分组
├── app.js // 项目入口文件
└── config.js // 配置文件
复制代码
依稀还得大学的时候学过 SQL,不行了毕业过久忘了,因此这里使用 MongoDB,也不作过多介绍,也没啥好说了,安装好了,增删改查...剩下的就是提升了!这里补充一下我用的可视化工具是 Robo 3T
若是你还不了解 MongoDB
的话,我这里简单写了一下如何安装使用 MongoDB
这里仍是看一下几张主要的表都长啥样吧:
const CourseSchema = new Schema({
id: {
type: String,
unique: true,
required: true
},
status: {
type: String
},
publisher: {
type: String,
required: true
},
publisherHeader: {
type: String
},
publisherName: {
type: String
},
studentId: {
type: String
},
schoolId: {
required: true,
type: String
},
school: {
type: String
},
phone: {
type: String
},
publishTime: {
type: String
},
closeTime: {
type: String
},
remark: {
type: String
},
receiver: {
type: String
},
receiverName: {
type: String
},
province: {
type: Number
},
college: {
type: String
},
major: {
type: String
},
courseName: {
type: String
},
courseTime: {
required: true,
type: String
},
courseClass: {
type: String
},
coursePlace: {
required: true,
type: String
},
reward: {
type: Number
},
hasName: {
type: Boolean
},
hasStuId: {
type: Boolean
},
hasPhone: {
type: Boolean
},
hasReward: {
type: Boolean
}
}, { collection: 'courses', versionKey: false});
复制代码
Tip: 算了算了,有点占地方,这里就看一张表吧,其它的在项目的 models
文件目录下有
工具和项目结构我们都搞完了,就开始写代码吧
咋们这个项目采用先后端分离的方式进行开发,为了开发的顺畅进行,咱们先调试一下:前端发个请求,后端接收消息,并从数据库中拿到数据响应给前端(咱们以post和get方法为例写两个接口),get请求获取数据,post请求插入一条数据;
想一下这里主要的问题应该就是跨域的问题了!再仔细一想,跨域也不能算什么问题吧...哈哈(强行有问题)废话很少说,开始:
启动一个 Node
服务链接数据库,后续的操做都是基于数据库的:
const Koa = require('koa');
// 这里是一些常量的配置文件
const config = require('./config');
const mongoose = require('mongoose');
const app = new Koa();
mongoose.connect(config.db, { useNewUrlParser: true }, err => {
if (err) {
console.error('Failed to connect to database');
} else {
console.log('Connecting database successfully');
}
});
app.listen(config.port);
复制代码
Tip: 启动后服务 node app.js
以后看到如图所示的打印就说明数据库链接成功了:
example
吧定义一下表结构,为了演示,咱们就定义为只有一个类型为 String
类型的字段:
在后端项目 models
目录下新建一个 example.js
文件来定义表结构;
const mongoose = require('mongoose');
// 这里的流程官网上有,讲的很清楚,每一步是干什么的
const Schema = mongoose.Schema;
const exampleSchema = new Schema({
msg: {
type: String,
required: true
},
}, {
collection: 'example', // 这里是为了不新建的表会带上 s 后缀
versionKey: false // 不须要 __v 字段,默认是加上的
});
module.exports = mongoose.model('example', exampleSchema);
复制代码
这里咱们先插入一条数据吧,这里为了方便,我直接使用前面提到的可视化工具 Robo 3T
插入一条 'Hello World' 数据:
example
表的控制器,用来暴露接口在 controllers
目录下新建一个 example_controller.js
:
// 引入刚才定义的表
const Example_col = require('./../models/example');
// get 请求返回全部数据
const getExample = async (ctx, next) => {
const req = ctx.request.body;
const examples = await Example_col.find({}, { _id: 0 });
ctx.status = 200;
ctx.body = {
msg: 'get request!!',
data: {
data: req,
examples,
}
}
}
// post 带一个 msg 参数,并插入数据库
const postExample = async (ctx, next) => {
const req = ctx.request.body;
ctx.status = 200;
if (!req.msg || typeof req.msg != 'string') {
ctx.status = 401;
ctx.body = {
msg: 'post request!!',
desc: `parameter error!!msg: ${req.msg}`,
data: req
}
return;
}
const result = await Example_col.create({msg: req.msg});
ctx.body = {
msg: 'post request!!',
desc: 'insert success!',
data: result
}
}
// 暴露出这两个方法,在路由中使用
module.exports = {
getExample,
postExample
}
复制代码
在 routes/api
目录下新建一个 example_router.js
文件,主要的做用就是定义接口的请求路径和方式:
// 引入路由模块并实例化
const Router = require('koa-router');
const router = new Router();
// 导如对应的控制器
const example_controller = require('./../../app/controllers/example_controller');
// 为控制器的方法定义请求路径和请求方式
router.get('/example/get', example_controller.getExample);
router.post('/example/post', example_controller.postExample);
module.exports = router;
复制代码
这个概念咱们就很少说了,这里把 Koa 的地址放在这吧
在入口文件 app.js
中增长两句话:
const example_router = require('./routes/api/example_router');
app.use(example_router.routes()).use(example_router.allowedMethods());
复制代码
从新启动服务 node app.js
这里就很少说了,有兴趣的能够直接看仓库的代码就行了,大体是两个接口有兴趣的能够看看我前面写的文章二次封装axios:
const getExample = params => {
return axios({
url: '/example/get',
method: 'get',
params
})
}
const postExample = data => {
return axios({
url: '/example/post',
method: 'post',
data
})
}
复制代码
为了方便展现结果,我直接在 前端的入口文件 App.vue
的生命周期钩子中使用:
// ... 此处代码省略
mounted() {
this.$http.getExample({name: 'frank'});
}
复制代码
启动前端项目: npm start
哎哟!报错了,不要慌仔细看看原来是跨域了呀,还记得前面说的强行有问题吗?我前面有说到在依赖的三方库里有一个叫 koa2-cors
,是时候该它上场了,咱们在 app.js
中做为中间件使用它:
解决跨域的方式有不少,但这不是咱们如今讨论的重点,这里我使用上述的 koa2-cors
,只须要将其做为中间件使用就行了,在 app.js
中添加:
const cors = require('koa2-cors');
app.use(cors());
复制代码
PS: 这里要注意一下,js 是单线程语言,中间件是有执行前后顺序的,因此 app.use(cors());
的使用必须在 router
以前,否则就没法解决跨域的问题哦!
好了从新启动服务:node app.js
examples
这个字段就是咱们刚刚在表里插入的数据,get
请求成功了,再来使用 post
请求向数据库里插入一条数据吧:
咱们仍是在 App.vue
中写:
mounted() {
// this.$http.getExample({name: 'frank'});
this.$http.postExample({msg: 'test post request!'});
}
复制代码
post
请求也成功了,好了如今来看看数据库的 example
表:
没毛病,目前!咱们已经成功实现先后端分离,而且已经打通了先后端的交互,后续的开发无非就是依葫芦画瓢拓展开发了。
文章开头我作了一下产品简介,正式开发以前就须要了解一下到底要作什么,作成什么样!画个流程图,或者作个 PRD
什么的,固然这些对于我来讲就算了,全靠 YY,我就大体说一下吧:
上面的预览图看得出来大体的结构,咱们主要分为五个模块(其实应该不算登陆就四个):
登陆模块兼注册,这里参考一下用户表(后端项目 models/user.js
文件),用户的密码我单独写了一个 password
表,用户id做为关联,密码是通过加密的,未使用明文。用户注册的时候不须要详细信息,可是发布课程就须要用户完善我的信息了(好比学校总得有吧),好比用户能够收藏课程,那么增长一个 collections
字段(类型为数组)用来存放课程的 id
代课模块主要就是展现课程的信息,用户点击以后能够查看详细的信息,固然部分信息是发布者但愿让你看到的才会展现,该模块包括后续的发布模块,课程模块都依赖于 courses
表,这里也很少赘述了...
发布课程须要用户完善必要的信息才能发布,发布课程也须要一些课程相关的必要信息(好比没有时间地点怎么上课?),固然为了一些其余的 PY 交易,发布者能够选择提供一些额外的信息或者备注。
课程模块,分为三个 tab,分别为我发布的、我代课的和我收藏的,点击以后会展现详细信息
个人模块,主要就是我的信息的展现,支持我的信息的修改,对应的表也就是用户表
以后就是正式开发了,我也就不重复的讲代码了,文章的开头结尾我会放上 git
仓库的地址,但愿你们点个 start
这将是我这个单身狗最大的快乐(题外话扯多了)
最大的问题就是数据来源,用户能够选择本身的学校(大学),所以须要全国的大学信息做为数据基础,全国高校名单 能够在这个网站查到,可是须要本身作一些调整,并且不够完善,原本打算用爬虫搞数据下来,可是我在网上看到某位大佬有全国高校的数据,因此...感谢大佬,让我节约了不少时间!(实在很差意思,我目前不知道是在哪看到的了,不能贴出大佬的地址,再次表示感谢),好了,基础数据有了剩下就是添砖加瓦,下面仍是看一下学校的数据结构(单条数据):
{
"_id" : ObjectId("5b7648965675dd2687a5b680"),
"id" : "3500",
"name" : "四川大学",
"website" : "http://www.scu.edu.cn/",
"provinceId" : 23,
"level" : "本科",
"abbreviation" : "scu",
"city" : "成都市"
}
复制代码
此外,还有一些全国城市的信息,省的信息等,这些信息我都和课程、用户作了关联方便后续开发的扩展,好比能够统计展现一些可视化图表等。
项目比较简单,可是也算是一次比较全的实践了,所用的框架技术(Vue、Koa、mongodb)等都是目前比较火的,对于初学者仍是比较有意义的(本人也是第一次用,这篇文章算是学习笔记了)。
不足之处:在 代课
模块应该区分一下用户应该默认获取同校的课程,固然能够加上查询其它或全部学校的课程,获取列表应该作分页,前段最好仍是下拉刷新...诸如此类的问题就很少说了!若是正在看文章的你有兴趣的话能够在此基础上优化。
此外,后端的日志,参数等统一处理也没有作...诸如此类的问题还有...
最后总结一下,学习才能进步...
但愿你不吝赐教,能够的话给颗 star
鼓励一下吧:
PS: 数据库导出的 json 文件我放在了后端仓库的 db-daike
目录下