前言
這篇我們來實現mock資料模擬介面,實現前後端分離開發,不用在追著後端大大要介面了~
開發
先來簡單介紹下我們用到的包吧:
名稱 | 簡介 |
---|---|
koa | 一種簡單好用的 Web 框架。它的特點是優雅、簡潔、表達力強、自由度高。 |
koa-router | 常用的 koa 的路由庫 |
koa-bodyparser | 用來解析body的中介軟體 |
mockjs | 生成隨機資料,模擬資料 |
讓我們來安裝這些包吧
npm install -D koa koa-router koa-bodyparser mockjs
複製程式碼
還記得我們上一張的目錄結構吧,讓我們在mock
資料夾下建立mock-server.js
const Koa = require('koa');
const router = require('koa-router')();
const bodyParser = require('koa-bodyparser');
const app = new Koa();
app.use(bodyParser())
const home = require('./home')
app.use(router.routes())
router.get('/list', async(ctx, next) => {
ctx.body = home
await next()
})
// error-handling
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
app.listen(3000); //這裡的埠要和webpack裡devServer的埠對應
console.log('app started at port 3000')
複製程式碼
注意哦, app.listen的埠要和webpack裡devServer的埠對應
然後我們在建立一個home.js
, 讓我們先使用mock文件給的例子
// 使用 Mock
var Mock = require('mockjs')
var data = Mock.mock( {
// 屬性 list 的值是一個陣列,其中含有 1 到 10 個元素
'list|1-10': [{
// 屬性 id 是一個自增數,起始值為 1,每次增 1
'id|+1': 1
}]
})
module.exports = data
複製程式碼
然後我們在package.json
的 scripts 新增啟動命令
"mock": "node ./mock/mock-server.js"
複製程式碼
好,讓我們執行命令列執行npm run mock
, 看到命令列輸出app started at port 3000
說明啟動成功了。我們可以發起請求來看看是不是我們home.js裡面模擬資料。
我使用的是fetch來發起請求,當然辦法有很多不一定要和我一樣
npm install whatwg-fetch -S
複製程式碼
讓我們在Home.jsx
裡面加入一些程式碼:
import React, {PureComponent} from 'react'
export default class Home extends PureComponent{
componentDidMount(){
fetch('api/list')
}
render(){
return (
<div>Hello World!</div>
)
}
}
複製程式碼
好了,現在讓我們在新的命令列裡面執行npm start
執行專案。在瀏覽器檢視是否有list請求發出和返回資料。
現在讓我們修改home.js
模擬資料,我們會發現請求的返回資料沒有改變,需要重啟koa的服務才可以,很麻煩,不是我們想要的,那怎麼辦呢? 讓我安裝nodemon
來解決吧
npm install -D nodemon
複製程式碼
修改mock
命令為
"mock": "nodemon ./mock/mock-server.js"
複製程式碼
執行npm run mock
後,在修改home.js
的模擬資料試試,是不是很完美。
好了,mock資料模擬介面我們已經實現了,不過我們的專案有點簡單,讓我們來加幾個頁面,順便把fetch封裝下吧。
在utils
資料夾下,建立request.js
import 'whatwg-fetch'
const codeMessage = {
200: '伺服器成功返回請求的資料。',
201: '新建或修改資料成功。',
202: '一個請求已經進入後臺排隊(非同步任務)。',
204: '刪除資料成功。',
400: '發出的請求有錯誤,伺服器沒有進行新建或修改資料的操作。',
401: '使用者沒有許可權(令牌、使用者名稱、密碼錯誤)。',
403: '使用者得到授權,但是訪問是被禁止的。',
404: '發出的請求針對的是不存在的記錄,伺服器沒有進行操作。',
406: '請求的格式不可得。',
410: '請求的資源被永久刪除,且不會再得到的。',
422: '當建立一個物件時,發生一個驗證錯誤。',
500: '伺服器發生錯誤,請檢查伺服器。',
502: '閘道器錯誤。',
503: '服務不可用,伺服器暫時過載或維護。',
504: '閘道器超時。',
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response
}
const errortext = codeMessage[response.status] || response.statusText
const error = new Error(errortext)
error.name = response.status
error.response = response
throw error
}
/**
*
* @param {string} url 請求url
* @param {object} [options] fetch 配置選項
* @return {object}
*/
export default function request(url, options) {
const defaultOptions = {
credentials: 'include',
}
const newOptions = { ...defaultOptions, ...options }
if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
if (!(newOptions.body instanceof FormData)) {
newOptions.headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
...newOptions.headers,
}
newOptions.body = JSON.stringify(newOptions.body)
} else {
newOptions.headers = {
Accept: 'application/json',
...newOptions.headers,
}
}
}
return fetch(url, newOptions)
.then(checkStatus)
.then(response => {
if (newOptions.method === 'DELETE' || response.status === 204) {
return response.text()
}
return response.json()
})
.catch(e => {
const status = e.name
console.log(status)
})
}
複製程式碼
這裡借鑑了antd-pro的fetch封裝。
在routes
資料夾下,建立Page1.jsx Page2.jsx Page3.jsx
修改Home.jsx
為
import React, { PureComponent } from 'react'
import { Route, Switch, Redirect, Link } from 'react-router-dom'
import Page1 from './Page1.jsx'
import Page2 from './Page2.jsx'
import Page3 from './Page3.jsx'
export default class Home extends PureComponent{
render(){
return (
<div>
<div style={styles.container}>
<Link to="/page1" style={styles.link} >Page1</Link>
<Link to="/page2" style={styles.link} >Page2</Link>
<Link to="/page3">Page3</Link>
</div>
<div style={styles.container}>
<Switch>
<Route path="/page1" component={Page1}/>
<Route path="/page2" component={Page2}/>
<Route path="/page3" component={Page3}/>
<Redirect exact from="/" to='/page1' />
</Switch>
</div>
</div>
)
}
}
const styles = {
container: {
display: 'flex',
justifyContent: 'center'
},
link: {
marginRight: 10
}
}
複製程式碼
Page1.jsx
為:
import React, {PureComponent} from 'react'
import request from '../utils/request'
export default class Page1 extends PureComponent{
state={
data: {}
}
click=()=>{
request('api/page1').then(data=>{
this.setState({
data: data
})
})
}
render(){
return (
<div>
<h1>Hello Page1!</h1>
<input type="button" value="獲取mcok" onClick={this.click} />
<div style={{width:300}}>
<p>Page1的mock模擬資料為:{JSON.stringify(this.state.data)}</p>
</div>
</div>
)
}
}
複製程式碼
Page2.jsx
為:
import React, {PureComponent} from 'react'
import request from '../utils/request'
export default class Page2 extends PureComponent{
state={
data: {}
}
click=()=>{
request('api/page2').then(data=>{
this.setState({
data: data
})
})
}
render(){
return (
<div>
<h1>Hello Page2!</h1>
<input type="button" value="獲取mcok" onClick={this.click} />
<div style={{width:300}}>
<p>Page2的mock模擬資料為:{JSON.stringify(this.state.data)}</p>
</div>
</div>
)
}
}
複製程式碼
Page3.jsx
為:
import React, {PureComponent} from 'react'
import request from '../utils/request'
export default class Page2 extends PureComponent{
state={
data: {}
}
click=()=>{
request('api/page3').then(data=>{
this.setState({
data: data
})
})
}
render(){
return (
<div>
<h1>Hello Page3!</h1>
<input type="button" value="獲取mcok" onClick={this.click} />
<div style={{width:300}}>
<p>Page3的mock模擬資料為:{JSON.stringify(this.state.data)}</p>
</div>
</div>
)
}
}
複製程式碼
修改 mock
資料夾下的home.js
為mock.js
const router = require('koa-router')();
var Mock = require('mockjs')
router.get('/page1', async (ctx, next) => {
ctx.body = Mock.mock({
data: [
{
"id": 1,
"title": "科學搬磚組",
"description": '那是一種內在的東西,他們到達不了,也無法觸及的',
},
{
"id": 2,
"title": "程式設計師日常",
"description": '那時候我只會想自己想要什麼,從不想自己擁有什麼',
},
{
"id": 3,
"title": "騙你來學計算機",
"description": '生命就像一盒巧克力,結果往往出人意料',
},
{
"id": 4,
"title": "全組都是吳彥祖",
"description": '希望是一個好東西,也許是最好的,好東西是不會消亡的',
},
],
status: 200,
})
})
router.get('/page2', async (ctx, next) => {
ctx.body = Mock.mock({
"data|1-10":[{
'id|+1': 1,
'number|1-100': 10.0
}]
})
})
router.get('/page3', async (ctx, next) => {
ctx.body = Mock.mock({
"data|1-10":[{
'id|+1': 1,
'email': '@email',
'name' : '@Name'
}]
})
})
module.exports = router
複製程式碼
mock-server.js
修改為
const Koa = require('koa');
const router = require('koa-router')();
const bodyParser = require('koa-bodyparser');
const app = new Koa();
const mock = require('./mock');
app.use(bodyParser())
app.use(mock.routes())
// error-handling
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
app.listen(3000);
console.log('app started at port 3000')
複製程式碼
好,現在我們在跑起來看看吧。page1的資料是不變的,page2和page3的資料每次請求都是隨機生成的。
是不是感覺每次要命令列啟動兩次很麻煩呢?我們當然可以整合成一個命令來啟動
npm install concurrently -D
複製程式碼
然後我們在package.json
的 scripts 新增新啟動命令
"dev": "concurrently \"npm run mock\" \"npm start\" "
複製程式碼
現在我們就可以使用npm run dev
一個命令來啟動了。
總結
本章主要介紹實現mock本地資料模擬介面,並對專案增加了些簡單頁面。
下篇文章我們來介紹開發環境webpack的配置