閒來無事,試了一下 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
目錄下