摘要:本項(xiàng)目中采用了進(jìn)行狀態(tài)管理,的主要作用是允許狀態(tài)在不同分支的組件中進(jìn)行傳遞,從而避免了使用原始方法如導(dǎo)致的不同分支組件之間數(shù)據(jù)無法傳遞子組件無法修改父組件狀態(tài)等問題。
項(xiàng)目功能
最近在做一個(gè)舊書交易網(wǎng)站,本屬于B/S體系結(jié)構(gòu)的課程作業(yè),但由于采用了新的框架所以躍躍欲試想都記錄下來。
</>復(fù)制代碼
實(shí)現(xiàn)一個(gè)舊書交易網(wǎng)站,基本功能如下:
實(shí)現(xiàn)用戶注冊(cè)、登錄功能,用戶注冊(cè)時(shí)需要填寫必要的信息并驗(yàn)證,如用戶名、密碼要求在6字節(jié)以上,email的格式驗(yàn)證,并保證用戶名和email在系統(tǒng)中唯一。
用戶登錄后可以發(fā)布要交易的書籍,需要編輯相關(guān)信息,包括書名、原價(jià)、出售價(jià)、類別和內(nèi)容介紹等信息、外觀照片等,可以通過ISBN和書名鏈接到外部系統(tǒng)(如Amazon/京東/當(dāng)當(dāng)?shù)染W(wǎng)站)的詳細(xì)介紹頁面。
根據(jù)用戶發(fā)布的書籍聚合生成首頁,可以分類檢索。
用戶可以設(shè)置交易模式為寄送還是線下交易,生成訂單時(shí)錄入不同內(nèi)容。
集成一個(gè)消息系統(tǒng),買家和賣家之間可以通信。
提供求購模塊,用戶可以發(fā)布自己想要的書籍。
界面樣式需要適配PC和手機(jī)的瀏覽器。
實(shí)現(xiàn)一個(gè)Android或iphone客戶端軟件,功能同網(wǎng)站,額外支持定位功能,發(fā)布時(shí)記錄位置,可以根據(jù)用戶的位置匹配最近的待售書籍。消息和訂單支持推送。
技術(shù)選型
數(shù)據(jù)庫
數(shù)據(jù)庫使用MySQL進(jìn)行開發(fā),因?yàn)榄h(huán)境之前都已經(jīng)配好了( ̄▽ ̄)"
后端經(jīng)過Express和Koa比對(duì),最終選擇Koa作為基于Node.js的Web開發(fā)框架。Koa是一個(gè)新的web框架,由Express幕后原班人馬打造,語法上也使用了ES6新的語法(例如丟棄了回調(diào)函數(shù)而使用async解決異步調(diào)用問題),看起來十分優(yōu)雅o( ̄▽ ̄)o
前端采用React+Semantic UI,由于之前對(duì)React有足夠多的實(shí)踐,因此本次重點(diǎn)還是放在后端開發(fā)及前后端連接上……
開發(fā)過程 參考教程Vue+Koa全棧開發(fā)
Koa框架教程 - 阮一峰
Koa框架搭建 初始化
命令行輸入
</>復(fù)制代碼
npm init -y
npm i koa koa-json
npm i -D nodemon
更改package.json內(nèi)容,將scripts中的內(nèi)容更改為"start":"nodemon app.js"
根目錄下新建app.js
</>復(fù)制代碼
const Koa = require("koa");
const json = require("koa-json");
const logger = require("koa-logger");
const KoaRouter = require("koa-router");
const parser = require("koa-bodyparser");
const app = new Koa();
const router = new KoaRouter();
// Json Prettier Middleware
app.use(json());
app.use(parser());
app.use(logger());
// Simple Middleware Example
// app.use(async ctx => (ctx.body = { msg: "Hello world" }));
app.listen(4113, () => console.log("----------Server Started----------"));
module.exports = app;
命令行輸入node app.js,瀏覽器打開localhost:3000查看返回?cái)?shù)據(jù)
sequelize連接數(shù)據(jù)庫
安裝包
</>復(fù)制代碼
npm install sequelize-auto -g
npm install tedious -g
npm install mysql -g
進(jìn)入src目錄,輸入sequelize-auto -o "./schema" -d bookiezilla -h 127.0.0.1 -u root -p 3306 -x XXXXX -e mysql,(其中 -o 參數(shù)后面的是輸出的文件夾目錄, -d 參數(shù)后面的是數(shù)據(jù)庫名, -h 參數(shù)后面是數(shù)據(jù)庫地址, -u 參數(shù)后面是數(shù)據(jù)庫用戶名, -p 參數(shù)后面是端口號(hào), -x 參數(shù)后面是數(shù)據(jù)庫密碼 -e 參數(shù)后面指定數(shù)據(jù)庫為mysql)
此時(shí)schema文件夾下會(huì)自動(dòng)生成三個(gè)表的文件,例如:
</>復(fù)制代碼
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define(
"book",
{
BookID: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
BookName: {
type: DataTypes.STRING(45),
allowNull: true
},
BookCostPrice: {
type: "DOUBLE",
allowNull: true
},
BookSalePrice: {
type: "DOUBLE",
allowNull: true
},
BookCategory: {
type: DataTypes.STRING(45),
allowNull: true
},
BookPhoto: {
type: DataTypes.STRING(45),
allowNull: true
},
BookContent: {
type: DataTypes.STRING(45),
allowNull: true
},
BookISBN: {
type: DataTypes.STRING(45),
allowNull: true
}
},
{
tableName: "book"
}
);
};
在serversrcconfig下新建文件database.js,用于初始化Sequelize和數(shù)據(jù)庫的連接。
</>復(fù)制代碼
const Sequelize = require("sequelize");
// 使用url連接的形式進(jìn)行連接,注意將root: 后面的XXXX改成自己數(shù)據(jù)庫的密碼
const BookieZilla = new Sequelize(
"mysql://root:XXXXX@localhost/bookiezilla",
{
define: {
timestamps: false// 取消Sequelzie自動(dòng)給數(shù)據(jù)表加入時(shí)間戳(createdAt以及updatedAt),否則進(jìn)行增刪改查操作時(shí)可能會(huì)報(bào)錯(cuò)
}
}
);
module.exports = {
BookieZilla // 將BookieZilla暴露出接口方便Model調(diào)用
};
為方便之后根據(jù)用戶id查詢信息,可先在數(shù)據(jù)庫中隨意增加一條數(shù)據(jù)。
在serversrcmodels下新建文件userModel.js,數(shù)據(jù)庫和表結(jié)構(gòu)文件連接起來。
</>復(fù)制代碼
const db = require("../config/database.js");
const userModel = "../schema/user.js";// 引入user的表結(jié)構(gòu)
const BookieZilla = db.BookieZilla;// 引入數(shù)據(jù)庫
const User = BookieZilla.import(userModel);// 用sequelize的import方法引入表結(jié)構(gòu),實(shí)例化了User。
const getUserById = async function(id) {
const userInfo = await User.findOne({
where: {
UserID: id
}
});
return userInfo;
};
module.exports = {
getUserById,
getUserByEmail
};
在serversrccontrollers下新建文件userController.js,來執(zhí)行這個(gè)方法,并返回結(jié)果。
</>復(fù)制代碼
Koa 提供一個(gè) Context 對(duì)象,表示一次對(duì)話的上下文(包括 HTTP 請(qǐng)求和 HTTP 回復(fù))。通過加工這個(gè)對(duì)象,就可以控制返回給用戶的內(nèi)容。
</>復(fù)制代碼
const user = require("../models/userModel.js");
const getUserInfo = async function(ctx) {
const id = ctx.params.id;// 獲取url里傳過來的參數(shù)里的id
const result = await user.getUserById(id);
ctx.body = result;// 將請(qǐng)求的結(jié)果放到response的body里返回
};
module.exports = {
getUserInfo,
vertifyUserLogin
};
在serversrc outes下新建文件auth.js,用于規(guī)劃auth下的路由規(guī)則。
</>復(fù)制代碼
const auth = require("../controllers/userController.js");
const router = require("koa-router")();
router.get("/user/:id", auth.getUserInfo);
module.exports = router;
回到根目錄下的app.js,將這個(gè)路由規(guī)則“掛載”到Koa上去。
</>復(fù)制代碼
const Koa = require("koa");
const json = require("koa-json");
const logger = require("koa-logger");
const KoaRouter = require("koa-router");
const parser = require("koa-bodyparser");
const auth = require("./src/routes/auth.js");// 引入auth
const app = new Koa();
const router = new KoaRouter();
// Json Prettier Middleware
app.use(json());
app.use(parser());
app.use(logger());
// Simple Middleware Example
// app.use(async ctx => (ctx.body = { msg: "Hello world" }));
// Router Middleware
router.use("/auth", auth.routes());// 掛載到koa-router上,同時(shí)會(huì)讓所有的auth的請(qǐng)求路徑前面加上"/auth"的請(qǐng)求路徑。
app.use(router.routes()).use(router.allowedMethods());// 將路由規(guī)則掛載到Koa上。
app.listen(4113, () => console.log("----------Server Started----------"));
module.exports = app;
API Test
SUCCESS!!!
前后端數(shù)據(jù)傳遞由于本項(xiàng)目采用的是前后端分離的架構(gòu),因此需要通過json來傳遞數(shù)據(jù),以實(shí)現(xiàn)登錄功能為例來闡述實(shí)現(xiàn)的具體步驟。
后端驗(yàn)證登錄
serversrcmodelsuserModel.js增加方法,用于通過郵箱查找用戶。
</>復(fù)制代碼
// ...
const getUserByEmail = async function(email) {
const userInfo = await User.findOne({
where: {
UserEmail: email
}
});
return userInfo;
};
module.exports = {
getUserById,
getUserByEmail
};
serversrccontrolleruserController.js增加方法,用于驗(yàn)證登錄信息并將結(jié)果以json形式返回給前端。
注意此處實(shí)際上應(yīng)用了JSON-WEB-TOKEN實(shí)現(xiàn)無狀態(tài)請(qǐng)求,關(guān)于jwt的原理和實(shí)現(xiàn)方法請(qǐng)參考這篇文章和這篇文章。
簡單來說,運(yùn)用了JSON-WEB-TOKEN的登錄系統(tǒng)應(yīng)該是這樣的:
用戶在登錄頁輸入賬號(hào)密碼,將賬號(hào)密碼(密碼進(jìn)行md5加密)發(fā)送請(qǐng)求給后端
后端驗(yàn)證一下用戶的賬號(hào)和密碼的信息,如果符合,就下發(fā)一個(gè)TOKEN返回給客戶端。如果不符合就不發(fā)送TOKEN回去,返回驗(yàn)證錯(cuò)誤信息。
如果登錄成功,客戶端將TOKEN用某種方式存下來(SessionStorage、LocalStorage),之后要請(qǐng)求其他資源的時(shí)候,在請(qǐng)求頭(Header)里帶上這個(gè)TOKEN進(jìn)行請(qǐng)求。
后端收到請(qǐng)求信息,先驗(yàn)證一下TOKEN是否有效,有效則下發(fā)請(qǐng)求的資源,無效則返回驗(yàn)證錯(cuò)誤。
使用前需要安裝相應(yīng)庫:
</>復(fù)制代碼
npm i koa-jwt jsonwebtoken util -s
此外,為保證安全性,后端數(shù)據(jù)庫的密碼不能采用明文保存,此處使用bcrypt的加密方式。
</>復(fù)制代碼
npm i bcryptjs -s
</>復(fù)制代碼
const user = require("../models/userModel.js");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const getUserInfo = async function(ctx) {
const id = ctx.params.id;
const result = await user.getUserById(id);
ctx.body = result;
};
const vertifyUserLogin = async function(ctx) {
const data = ctx.request.body; // post過來的數(shù)據(jù)存在request.body里
const userInfo = await user.getUserByEmail(data.email);
if (userInfo != null) { // 如果查無此用戶會(huì)返回null
if (!bcrypt.compareSync(data.psw, userInfo.UserPsw) {
ctx.body = {
status: false,
msg: "Wrong password"
};
} else { // 如果密碼正確
const userToken = {
id: userInfo.UserID,
email: userInfo.UserEmail
};
const secret = "react-koa-bookiezilla"; // 指定密鑰,這是之后用來判斷token合法性的標(biāo)志
const token = jwt.sign(userToken, secret); // 簽發(fā)token
ctx.body = {
status: true,
token: token // 返回token
};
}
} else {
ctx.body = {
status: false,
msg: "User doesn"t exist"
};
}
};
module.exports = {
getUserInfo,
vertifyUserLogin
};
更新serversrc outesauth.js中的路由規(guī)則。
</>復(fù)制代碼
const auth = require("../controllers/userController.js");
const router = require("koa-router")();
router.get("/user/:id", auth.getUserInfo);
router.post("/login", auth.vertifyUserLogin);
module.exports = router;
前端校驗(yàn)數(shù)據(jù)并發(fā)送請(qǐng)求
前端主要使用了react-router進(jìn)行路由跳轉(zhuǎn),使用semantic-ui作為UI組件庫,使用axios發(fā)送請(qǐng)求,Login.js代碼如下:
</>復(fù)制代碼
import React, { Component } from "react";
import {
Button,
Form,
Grid,
Header,
Image,
Message,
Segment,
Loader
} from "semantic-ui-react";
import { NavLink, withRouter } from "react-router-dom";
import axios from "axios";
import Logo from "../images/logo.png";
class Login extends Component {
state = {
email: "",
psw: "",
alert: false,
load: false
};
vertifyFormat = () => {
var pattern = /^([A-Za-z0-9_-.])+@([A-Za-z0-9_-.])+.([A-Za-z]{2,4})$/;
return pattern.test(this.state.email) && this.state.psw.length >= 6;
};
sendLoginRequest = () => {
if (this.vertifyFormat()) {
this.setState({
alert: false,
load: true
});
axios
.post("/auth/login", {
email: this.state.email,
psw: this.state.psw
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
} else {
this.setState({
alert: true
});
}
};
render() {
var alert =
this.state.alert === false ? (
) : (
);
var load = this.state.load === false ? : ;
return (
Log-in to your B::kzilla
{
this.setState({
email: event.target.value
});
}}
/>
{
this.setState({
psw: event.target.value
});
}}
/>
{alert}
{load}
Login
New to us?
Sign Up
);
}
}
export default withRouter(Login);
React配置代理
安裝http-proxy-middleware中間件。
</>復(fù)制代碼
npm install http-proxy-middleware -s
create-react-app初始化的項(xiàng)目需要eject,使基本配置暴露出來。
</>復(fù)制代碼
npm run eject
clientsrc下新建文件setupProxy.js,配置代理轉(zhuǎn)發(fā)信息。
</>復(fù)制代碼
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
proxy("/api", {
target: "http://localhost:4113",
changeOrigin: true
})
);
app.use(
proxy("/auth", {
target: "http://localhost:4113",
changeOrigin: true
})
);
};
clientscriptsstart.js中進(jìn)行配置,在const devServer = new WebpackDevServer(compiler, serverConfig);后添加語句require("../src/setupProxy")(devServer);
發(fā)送請(qǐng)求格式如下:
</>復(fù)制代碼
axios
.post("/auth/login", {
email: this.state.email,
psw: this.state.psw
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
喜聞樂見的測試環(huán)節(jié)!
設(shè)計(jì)原理 數(shù)據(jù)庫 User*UserID | UserName | UserPsw | *UserEmail |
---|---|---|---|
INT | VARCHAR(45) | VARCHAR(45) | VARCHAR(45) |
</>復(fù)制代碼
CREATE TABLE `bookiezilla`.`user` (
`UserID` INT NOT NULL,
`UserName` VARCHAR(45) NULL,
`UserPsw` VARCHAR(45) NULL,
`UserEmail` VARCHAR(45) NOT NULL,
PRIMARY KEY (`UserID`, `UserEmail`));
Book
*BookID | BookName | BookCostPrice | BookSalePrice | BookCategory | BookPhoto | BookContent | BookISBN | BookRefs |
---|---|---|---|---|---|---|---|---|
INT | VARCHAR(45) | DOUBLE | DOUBLE | VARCHAR(45) | VARCHAR(45) | VARCHAR(45) | VARCHAR(45) | VARCHAR(45) |
</>復(fù)制代碼
CREATE TABLE `bookiezilla`.`book` (
`BookID` INT NOT NULL,
`BookName` VARCHAR(45) NULL,
`BookCostPrice` DOUBLE NULL,
`BookSalePrice` DOUBLE NULL,
`BookCategory` VARCHAR(45) NULL,
`BookPhoto` VARCHAR(45) NULL,
`BookContent` VARCHAR(45) NULL,
`BookISBN` VARCHAR(45) NULL,
PRIMARY KEY (`BookID`));
Order
*OrderID | *UserID | *BookID | TradeMethod | TradeStatus | TradeParty | TraderID |
---|---|---|---|---|---|---|
INT | INT | INT | VARCHAR(45) | VARCHAR(45) | VARCHAR(45) | INT |
</>復(fù)制代碼
CREATE TABLE `bookiezilla`.`order` (
`OrderID` INT NOT NULL,
`UserID` INT NOT NULL,
`BookID` INT NOT NULL,
`TradeMethod` VARCHAR(45) NULL,
`TradeStatus` VARCHAR(45) NULL,
`TraderID` INT NULL,
PRIMARY KEY (`OrderID`));
前端
目錄結(jié)構(gòu)
</>復(fù)制代碼
.
│ .gitignore
│ package-lock.json
│ package.json
│ README.md
│ yarn.lock
│
├─config // 基本配置文件
│ │ env.js
│ │ modules.js
│ │ paths.js
│ │ pnpTs.js
│ │ webpack.config.js
│ │ webpackDevServer.config.js
│ │
│ └─jest
│ cssTransform.js
│ fileTransform.js
│
├─public
│ favicon.ico
│ index.html
│ manifest.json
│
├─scripts // eject后生成的文件配置
│ build.js
│ start.js
│ test.js
│
└─src // 主要頁面及組件部分
│ App.css
│ App.js
│ index.css
│ index.js
│ serviceWorker.js
│ setupProxy.js // 設(shè)置代理轉(zhuǎn)發(fā),解決跨域問題
│
├─actions // react-redux需要定義的actions
│ UpdateActions.js
│
├─components // 頁面的組件部分
│ BookList.jsx
│ BookMarket.jsx
│ FeedBack.jsx
│ OrderInfo.jsx
│ PublishForm.jsx
│ SearchBar.jsx
│ SideMenu.jsx
│ StatisticData.jsx
│ StepFlow.jsx
│
├─images // 項(xiàng)目中使用的圖片資源
│ logo.png
│ matthew.png
│
├─pages // 頁面部分
│ Home.jsx
│ Login.jsx
│ Market.jsx
│ Message.jsx
│ Publish.jsx
│ Signup.jsx
│
└─reducers // react-redux需要定義的reducers
rootReducer.js
實(shí)現(xiàn)細(xì)節(jié)
項(xiàng)目中使用了react-router來控制路由,基本原理如下:
在App.js中引入路由對(duì)應(yīng)的頁面或組件,并引入react-router-dom中的BrowserRouter、Route、Switch組件進(jìn)行定義。
</>復(fù)制代碼
// App.jsx
import React, { Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import SideMenu from "./components/SideMenu";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Market from "./pages/Market";
import Publish from "./pages/Publish";
import Message from "./pages/Message";
import OrderInfo from "./components/OrderInfo";
class App extends Component {
render() {
return (
{/* Only match one */}
);
}
}
export default App;
當(dāng)項(xiàng)目頁面中需要進(jìn)行頁面跳轉(zhuǎn)時(shí),可使用react-router-dom中的withRouter將組件包裹起來,再使用NavLink進(jìn)行跳轉(zhuǎn)。
</>復(fù)制代碼
// Login.jsx
import { NavLink, withRouter } from "react-router-dom";
class Login extends Component {
.....
sendLoginRequest = () => {
......
this.props.history.push("/home");
render(){
......
}
};
export default withRouter(Login);
本項(xiàng)目中采用了react-redux進(jìn)行狀態(tài)管理,redux的主要作用是允許狀態(tài)在不同分支的組件中進(jìn)行傳遞,從而避免了使用原始方法(如this.props)導(dǎo)致的不同分支組件之間數(shù)據(jù)無法傳遞、子組件無法修改父組件狀態(tài)等問題。具體使用方法如下:
在src educers下新建文件rootReducer.js用于更新中心狀態(tài)樹中的信息。
</>復(fù)制代碼
// rootReducer.js
const initState = {
id: null,
token: null
};
const rootReducer = (state = initState, action) => {
if (action.type === "UPDATE_ID") {
return {
...state,
id: action.id
};
}
if (action.type === "UPDATE_TOKEN") {
return {
...state,
token: action.token
};
}
return state;
};
export default rootReducer;
在srcactions中新建文件UpdateActions.js用于定義行為。
</>復(fù)制代碼
// UpdateActions.js
export const updateId = id => {
return {
type: "UPDATE_ID",
id: id
};
};
export const updateToken = token => {
return {
type: "UPDATE_TOKEN",
token: token
};
};
在srcindex.js中使用react-redux中的組件對(duì)項(xiàng)目入口文件進(jìn)行包裹,并在全局范圍內(nèi)建立狀態(tài)樹。
</>復(fù)制代碼
// index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import "semantic-ui-css/semantic.min.css";
import { createStore } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./reducers/rootReducer";
const store = createStore(rootReducer);
ReactDOM.render(
,
,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
當(dāng)需要更新狀態(tài)樹中的信息時(shí),使用引入的action作為函數(shù)進(jìn)行更新。
</>復(fù)制代碼
// Login.jsx
import { connect } from "react-redux";
import { updateId, updateToken } from "../actions/UpdateActions";
class Login extends Component {
......
sendLoginRequest = () => {
......
this.props.updateId(res.data.id);
this.props.updateToken(res.data.token);
......
};
}
const mapStateToProps = state => {
return {};
};
const mapDispatchToProps = dispatch => {
return {
updateToken: token => {
dispatch(updateToken(token));
},
updateId: id => {
dispatch(updateId(id));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(Login));
當(dāng)需要使用狀態(tài)樹中的信息時(shí),先調(diào)用react-redux中的connect包裹組件,再使用this.props直接調(diào)用即可。
</>復(fù)制代碼
// PublishForm.jsx
import { connect } from "react-redux";
class PublishForm extends Component {
......
var UserID = this.props.id;
var UserToken = this.props.token;
......
}
const mapStateToProps = state => {
return {
id: state.id,
token: state.token
};
};
export default connect(mapStateToProps)(PublishForm);
后端
目錄結(jié)構(gòu)
</>復(fù)制代碼
.
│ app.js
│ package-lock.json
│ package.json
│
└─src
├─config // 數(shù)據(jù)庫配置
│ database.js
│
├─controllers // 控制器,獲取請(qǐng)求數(shù)據(jù)并調(diào)用models中的方法進(jìn)行處理并返回結(jié)果
│ apiController.js
│ msgController.js
│ userController.js
│
├─models // 實(shí)例模型,主要使用Sequelize定義的方法對(duì)數(shù)據(jù)庫進(jìn)行增刪改查
│ bookModel.js
│ CommentModel.js
│ orderModel.js
│ userModel.js
│
├─routes // 路由,不同文件對(duì)應(yīng)不同類型的api接口,分別與授權(quán)、功能實(shí)現(xiàn)、信息傳遞有關(guān)
│ api.js
│ auth.js
│ msg.js
│
└─schema // 數(shù)據(jù)庫表結(jié)構(gòu),可使用Sequelize自動(dòng)生成
book.js
comment.js
order.js
user.js
實(shí)現(xiàn)細(xì)節(jié)
當(dāng)Koa后端監(jiān)聽的端口接收到請(qǐng)求時(shí),會(huì)根據(jù)app.js中的路由規(guī)則進(jìn)行處理,我們將不同類型的接口定義在不同文件中,再通過router.use()進(jìn)行調(diào)用,避免發(fā)生接口冗亂復(fù)雜的情況。
</>復(fù)制代碼
// app.js
const Koa = require("koa");
const json = require("koa-json");
const logger = require("koa-logger");
const KoaRouter = require("koa-router");
const parser = require("koa-bodyparser");
const auth = require("./src/routes/auth.js");
const api = require("./src/routes/api.js");
const msg = require("./src/routes/msg.js");
const app = new Koa();
const router = new KoaRouter();
// Json Prettier Middleware
app.use(json());
app.use(parser());
app.use(logger());
// Simple Middleware Example
// app.use(async ctx => (ctx.body = { msg: "Hello world" }));
// Router Middleware
router.use("/auth", auth.routes());
router.use("/msg", msg.routes());
router.use("/api", api.routes());
app.use(router.routes()).use(router.allowedMethods());
app.listen(4113, () => console.log("----------Server Started----------"));
module.exports = app;
</>復(fù)制代碼
// auth.js
const auth = require("../controllers/userController.js");
const router = require("koa-router")();
router.get("/user/:id", auth.getUserInfo);
router.post("/login", auth.vertifyUserLogin);
router.post("/signup", auth.signupNewUser);
module.exports = router;
</>復(fù)制代碼
// api.js
const api = require("../controllers/apiController.js");
const router = require("koa-router")();
router.get("/getbooks", api.getAllBooks);
router.get("/getorder/:id", api.getOrderInfo);
router.post("/searchbooks", api.searchBooks);
router.post("/publish", api.publishNewBook);
router.post("/confirmorder", api.updateOrderOfTrade);
module.exports = router;
</>復(fù)制代碼
// msg.js
const msg = require("../controllers/msgController.js");
const router = require("koa-router")();
router.get("/getcomments", msg.getAllComments);
router.post("/newcomment", msg.publishNewComment);
module.exports = router;
項(xiàng)目成果
登錄注冊(cè)
Bookizilla能夠?qū)崿F(xiàn)用戶注冊(cè)、用戶登錄功能,其中對(duì)用戶注冊(cè)時(shí)需要的數(shù)據(jù)做了格式處理(如驗(yàn)證Email格式、保證兩次密碼輸入數(shù)據(jù)相符且不小于6字節(jié)等)。如果用戶在注冊(cè)過程中出現(xiàn)錯(cuò)誤,則會(huì)出現(xiàn)相應(yīng)提示以指導(dǎo)用戶進(jìn)行正確輸入。
Login.jsx
Signup.jsx
個(gè)人主頁Bookiezilla的主頁呈現(xiàn)的是與該用戶有關(guān)的信息數(shù)據(jù)(如FAVES、VIEWS等,但由于目前后端并未儲(chǔ)存相關(guān)數(shù)據(jù)所以暫用了mocks)及該用戶所發(fā)布的所有書籍。
Home.jsx
書籍市場Bookiezilla的書籍市場呈現(xiàn)了所有用戶發(fā)布的所有書籍,用戶可以使用上方的搜索框輸入關(guān)鍵詞(如書名、標(biāo)簽 、ISBN等)。用戶還可點(diǎn)擊圖書下方按鈕以查看具體信息,進(jìn)而決定是否達(dá)成交易,也可點(diǎn)擊鏈接在Amazon中查看書籍的詳細(xì)介紹。
Market.jsx
書籍發(fā)布Bookiezilla允許用戶發(fā)布書籍,并設(shè)置訂單的關(guān)鍵信息(如書籍基本信息、交易模式、尋求買家或賣家等)。需要注意的是,由于書籍發(fā)布和書籍求購很大一部分內(nèi)容是重合的,所以此處將二者合并并且給出TradeParty選項(xiàng)來使用戶選擇是想要發(fā)布書籍還是求購書籍。
Publish.jsx
信息發(fā)布Bookiezilla設(shè)置了信息發(fā)布面板,用于用戶之間的溝通交流、信息發(fā)布等。用戶可直接發(fā)布評(píng)論或回復(fù)他人的評(píng)論,從而進(jìn)行持續(xù)性的交流。
Message.jsx
https://github.com/Sylvie-Hsu...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/106745.html
Github上的腳手架實(shí)在太多,可能大多數(shù)都是只專注在前端的web開發(fā),例如流行的React生態(tài)中的create-react-app和Vue生態(tài)中的Vue-cli, 但是可能作為像我一樣的全棧開發(fā),一個(gè)只關(guān)注在前端開發(fā)的腳手架滿足不了所有的需求,我們可能需要開發(fā)更復(fù)雜的全棧JS的項(xiàng)目,所以這里介紹又一個(gè)基于NodeJS的全棧開發(fā)框架 koa-web-kit,不一定適合所有人,但至少又多了個(gè)選擇?。...
稍微整理了一下自己平時(shí)看到的前端學(xué)習(xí)資源,分享給大家。 html MDN:Mozilla開發(fā)者網(wǎng)絡(luò) SEO:前端開發(fā)中的SEO css 張鑫旭:張鑫旭的博客 css精靈圖:css精靈圖實(shí)踐 柵格系統(tǒng):詳解CSS中的柵格系統(tǒng) 媒體查詢:css媒體查詢用法 rem布局:手機(jī)端頁面自適應(yīng)布局 移動(dòng)前端開發(fā)之viewport的深入理解:深入理解viewport 淘寶前端布局:手機(jī)淘寶移動(dòng)端布局 fl...
摘要:是一個(gè)基于和的服務(wù)器端和瀏覽器端的的前后端全棧應(yīng)用框架。是的組件,并且會(huì)進(jìn)行數(shù)據(jù)初始化不但可以支持的數(shù)據(jù)初始化,還可以合并和的,使用同一個(gè),和的無縫結(jié)合。 koa-cola是一個(gè)基于koa和react的服務(wù)器端SSR(server side render)和瀏覽器端的SPA(single page application)的web前后端全棧應(yīng)用框架。 koa-cola使用typescr...
摘要:序列文章從項(xiàng)目中由淺入深的學(xué)習(xí)微信小程序和快應(yīng)用從項(xiàng)目中由淺入深的學(xué)習(xí)從項(xiàng)目中由淺入深的學(xué)習(xí)前言的出現(xiàn)前端已經(jīng)可以用一把梭從前端寫到后臺(tái)。 showImg(https://segmentfault.com/img/bVbrRI5?w=1920&h=1080); 序列文章 從項(xiàng)目中由淺入深的學(xué)習(xí)vue,微信小程序和快應(yīng)用 (1)從項(xiàng)目中由淺入深的學(xué)習(xí)react (2)從項(xiàng)目中由淺入深的學(xué)...
摘要:搭建的博客曾經(jīng)用的寫的博客,現(xiàn)在看來已經(jīng)很了,所以用目前最火的框架重構(gòu)一下。后端重構(gòu)博客嘛,以前用寫的后臺(tái),所以略懂一些,作為一個(gè)前端開發(fā),目標(biāo)就是全棧嘛,選用了最為流行的也用了目前最為流行的作為后端配合。 React-Node搭建的博客 曾經(jīng)用的php+mysql+js寫的博客,現(xiàn)在看來已經(jīng)很low了,所以用目前最火的react+koa框架重構(gòu)一下。先上地址吧:目前線上版本http:...
閱讀 2779·2021-10-14 09:42
閱讀 834·2021-10-11 10:57
閱讀 780·2019-08-30 15:54
閱讀 1922·2019-08-30 13:50
閱讀 1691·2019-08-30 11:19
閱讀 938·2019-08-29 12:38
閱讀 1429·2019-08-26 11:51
閱讀 1398·2019-08-26 10:48