前言
不知道各位前端們在日常的開發(fā)生產(chǎn)中是否有過像我一樣的困擾
- 公司用的內(nèi)網(wǎng)或特定網(wǎng)絡(luò),離開公司就沒辦法使用內(nèi)部接口?
- 大屏項目或各種向領(lǐng)導(dǎo)演示時擔(dān)心網(wǎng)絡(luò)不穩(wěn)定,導(dǎo)致頁面的數(shù)據(jù),echart圖表等無法顯示?
- 在沒有后端沒有開發(fā)完的情況下怎樣自己實時制造接口數(shù)據(jù)來保證自己前端開發(fā)的進度不落后?
..........
其實總結(jié)就是缺少了一個我們前端可以控制的接口服務(wù)
先看效果~
-
node服務(wù)啟動前 依賴內(nèi)網(wǎng)環(huán)境的前端項目所有的接口都無法使用
image -
node服務(wù)啟動后 在內(nèi)網(wǎng)狀態(tài)下進行緩存后即可在脫離內(nèi)網(wǎng)環(huán)境下演示和調(diào)試頁面
image -
node服務(wù)在有緩存后可以保證向服務(wù)請求時不用等待??后端數(shù)據(jù)庫的查詢,接口往往在10ms以內(nèi)就可以返回,保證演示的時候界面不會出現(xiàn)因為等待某個接口而造成頁面圖表或文字顯示不全
image
開始手寫代碼
思路說明
- 其實并不是什么高大尚的東西,本質(zhì)就是在有網(wǎng)絡(luò)環(huán)境的情況下啟動一個node服務(wù),請求后將所有請求的數(shù)據(jù)保存在本地的json文件中,之后如果在缺乏網(wǎng)絡(luò)環(huán)境或者需要脫離后端自行調(diào)試的時候就可以完全由node服務(wù)獲取接口數(shù)據(jù)。
目錄構(gòu)建
由于是個簡單的項目,在這里我也就不使用koa,egg等框架了,有需要的小伙伴可以自行引入
- 先執(zhí)行
npm init
初始化一下文件夾?? - 在文件夾新建
index.js
作為入口文件,新建utils文件夾和其下的store.js
,新建json文件夾和其下的test.json
至此簡單的目錄結(jié)構(gòu)就已經(jīng)搭建成功啦。
├── index.js //主入口文件
├── json
│ └── test.json //存儲返回數(shù)據(jù)
├── package.json
└── utils
└── store.js //處理相關(guān)邏輯
代碼編寫
- store.js
// const fs = require('fs');
const date = new Date()
const path = require('path')
const defalutpath = path.resolve(__dirname, '../json/test.json')
const { promises: { readFile, writeFile } } = require('fs');
async function getStuff (path = defalutpath) {
let result = await readFile(path, 'utf8');
return result
}
async function setStuff (value, path = defalutpath) {
let result = await writeFile(path, value, 'utf8');
return result
}
const getJson = ((key, callback) => {
getStuff().then(res => {
const json = res ? JSON.parse(res) : {}
// console.log('讀取數(shù)據(jù)', json[key])
// console.log(json[key])
if (typeof (json[key]) !== "undefined") {
// console.log('有數(shù)據(jù)', json[key])
callback(json[key])
} else {
callback(false)
}
})
})
const setJson = ((key, value = {}) => {
getStuff().then(res => {
const json = res ? JSON.parse(res) : {}
json[key] = value
if (json !== undefined) {
setStuff(JSON.stringify(json))
console.log('寫入成功')
}
})
})
//如果有需要還可以在存儲的key值后面加上今天的日期,保證數(shù)據(jù)的有效期。過了有效期重新請求
const formatDate = ((date) => {
let myyear = date.getFullYear()
let mymonth = date.getMonth() + 1
let myweekday = date.getDate()
if (mymonth < 10) {
mymonth = "0" + mymonth
}
if (myweekday < 10) {
myweekday = "0" + myweekday
}
return myyear + "-" + mymonth + "-" + myweekday
})
const store = {
getJson,
setJson
}
module.exports = store
-
getStuff
和setStuff
來異步讀寫文件內(nèi)容,避免造成node堵塞 -
getJson
和setJson
則是對文件讀取寫入的邏輯處理
- index.js
// 1、加載模塊
const http = require('http');
const server = new http.Server()
const axios = require('axios');
const store = require('./utils/store')
const md5 = require('md5-node')
// const querystring = require('querystring')
const Url = require('url')
//定義請求頭,解決跨域
const headers = {
"Content-Type": "application/json;charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"access-control-allow-credentials": "true"
}
// 2、監(jiān)聽請求事件
server.on('request', function (request, response) {
// 監(jiān)聽到請求之后所做的操作
// request 對象包含:用戶請求報文的所有內(nèi)容
// response 響應(yīng)對象,用來響應(yīng)一些數(shù)據(jù)
// 當(dāng)服務(wù)器想要向客戶端響應(yīng)數(shù)據(jù)的時候,就必須使用response對象
// const { method, url, headers } = request;
// console.log(method, url, headers)
if (request.url !== '/favicon.ico') {
const index = request.url.indexOf('/mock/') //與前端約定用mock來識別
const reurl = 'http://' + request.url.substr(index + 6)
// 3、通過method來做不同的判斷
if (request.method === 'OPTIONS') {
response.writeHead(200, {
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "authorization,content-type,token",
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Expose-Headers": "authorization,content-type,token",
"Access-Control-Max-Age": "3600",
"Connection": "keep-alive",
"Content-Length": "0",
"Server": 'openresty/1.15.8.2'
})
// res.write('')
response.end()
console.log('options go api... ')
}
if (request.method === 'GET') {
store.getJson(reurl, ((res) => {
// console.log('GET-res', res)
if (res) {//如果命中緩存??
console.log('get請求命中緩存??')
response.writeHead(200, headers)
response.end(JSON.stringify(res))
} else {
console.log('get請求命中發(fā)送······')
axios.get(reurl)
.then(res => {
store.setJson(reurl, res.data)
response.writeHead(200, headers);
response.end(JSON.stringify(res.data));
})
.catch(err => {
console.log('geterr', err);
})
}
}))
} else {//post 請求
const newURL = Url.parse(reurl)
parseJSON(request, response, ((res) => {
const key = reurl + md5(res)//post請求用Md5加密保證參數(shù)獨立性
const postData = res
store.getJson(key, (res => {
//這里是如果請求頭中帶有token或各種自定義請求頭時的配置
const opheaders = {
// 'Content-Length': length,
// 'token': request.headers.token !== undefined ? request.headers.token : '',
'Content-Type': 'application/json;charset=UTF-8'
}
// store.getJson(reurl)
if (res) {//如果命中緩存??
console.log('post請求命中緩存??')
response.writeHead(200, headers)
response.end(JSON.stringify(res))
} else {
console.log('post請求命中....')
axios.post(reurl, postData, { headers: opheaders })
.then(function (res) {
store.setJson(key, res.data)
response.writeHead(200, headers);
response.end(JSON.stringify(res.data));
})
.catch(function (err) {
console.log('posterr', err);
});
}
}))
}))
}
}
})
function parseJSON (req, res, next) {
let data = ''
// const length = req.headers['content-length']
req.on('data', function (chunk) {
// chunk 默認是一個二進制數(shù)據(jù),和 data 拼接會自動 toString
data += chunk;
});
//注冊end事件,所有數(shù)據(jù)接收完成會執(zhí)行一次該方法
//如果需要可以使用querystring對url進行反序列化(解析url將&和=拆分成鍵值對),得到一個對象
req.on('end', function () {
// console.log(data)
if (data) {
data = JSON.parse(data)
}
next(data)
})
}
// 4、監(jiān)聽端口,開啟服務(wù)
server.listen(8099, function () {
console.log("服務(wù)器已經(jīng)啟動,可訪問以下地址:");
console.log('http://localhost:8099');
})
- 安裝依賴
axios,md5-node
,入口文件的思路很簡單,在收到請求后先截取/mock/
后的url用來作為store
的key值。 - 在本地json中讀取到這個key值則直接返回value值不去請求后端接口,如果沒有key值則請求后端接口并將后端的返回值寫入本地的json文件中,之后再返回給前端。
服務(wù)的啟動與使用
-
npm install supervisor -g
安裝這個依賴可以使我們服務(wù)熱更新便于調(diào)試。
image 運行
npm run hot
此時項目已經(jīng)啟動-
前端服務(wù)在原先的地址上加入此時node地址,例如原先前端請求地址為
172.16.18.147:8080/xxx/xxxx
則此時在前端axios請求配置為
http://node服務(wù)ip:8099/mock/172.16.18.147:8080/xxx/xxxx
image -
頁面正常的接口請求后,所有的返回數(shù)據(jù)會保存在本地的json文件中
image -
服務(wù)開啟時,如果請求命中??緩存則不會再走網(wǎng)絡(luò)請求,而是直接獲取本地json中的值
image
小結(jié)
至此node服務(wù)的功能大致已經(jīng)介紹完畢啦。以后再有演示或者脫離后端的情況下只要你開啟node服務(wù)
就可以用本地緩存來展示數(shù)據(jù)啦~
做完這個后又想著做一個electron版本的服務(wù)了,這樣就可以時時自定義請求頭和接口參數(shù),這部分就下次再更新了
這是我的github項目地址有需要的朋友們可以去看看然后點個star~