React 腳手架項目,使用mock數據,模擬後端數據

使用create-react-app 腳手架創建的項目,並沒有集成mock數據的功能,我們在進行前端開發的時候,如果後端服務器暫時沒有開發完成,我們就需要使用mock來模擬後端數據,以保證開發的進度.
由於腳手架項目是使用webpack進行打包的,我們就可以利用webpack的devServer的配置,來攔截我們所發送的ajax請求,並返回我們預先設置好的數據

暴露webpack設置 @craco/craco

腳手架創建的項目,默認狀態下是不會顯示webpack配置選項的,通常我們可以使用 npm run eject 命令來暴露出webpack的配置選項,但是一旦使用這個命令,項目中會多出非常多的文件,而且一旦使用該命令,就無法反悔,如果我們僅僅想要改動一兩個地方的配置,顯然有點得不償失,
幸好社區爲我們提供了一個非常好用的工具 @craco/craco
鏈接: https://www.npmjs.com/package/@craco/craco.
它可以讓我們在不暴露webpack的情況下進行webpack的配置
使用命令將craco安裝到你的項目中

npm install @craco/craco --save

在項目的根目錄中創建一個配置文件 craco.config.js
在這裏插入圖片描述
這個是默認的craco的配置文件的位置

當然,如果你想要指定配置文件的位置也是可以的,在package.json中可以單獨配置craco的配置文件位置,比如

/* package.json */
 
{
    "cracoConfig": "config/craco-config-with-custom-name.js"
}

一般我們用默認位置就好了

修改package.json文件,改成使用craco來運行項目
將原先的react-scripts改成craco
在這裏插入圖片描述

在craco.config.js文件中輸入以下內容

/* craco.config.js */
const apiMocker = require('mocker-api')//可以使用mocker-api庫
const path = require('path')

module.exports = {
    devServer: {
    	//如果不使用mocker-api庫
   		//before: require('./mock/index')  

		//如果使用mocker-api庫
        before(app)
        {
            apiMocker(app, path.resolve('./mock/index.js'), {
                
            })
        }
    }
};

在項目根目錄下創建mock文件夾

然後在項目根目錄下創建mock文件夾,在這個文件夾裏面,我們就可以編寫mock數據
在這裏插入圖片描述
通常我會把不同的api請求,放在不同的文件中,比如user相關的寫在user.js中,login相關的寫在login.js中
書寫格式如下:
user.js

module.exports = {
    // =====================
    // The default GET request.
    // https://github.com/jaywcjlove/mocker-api/pull/63
    '/api/user': {
        id: 1,
        username: 'kenny',
        sex: 6
    },
    
    'GET /api/user/list': [
        {
            id: 1,
            username: 'kenny',
            sex: 6
        }, {
            id: 2,
            username: 'kenny',
            sex: 6
        }
    ],
    
    
    'DELETE /api/user/:id': (req, res) =>
    {
        console.log('---->', req.body)
        console.log('---->', req.params.id)
        res.send({ status: 'ok', message: '刪除成功!' });
    }
}

login.js

module.exports = {
    'POST /api/login/account': (req, res) =>
    {
        const { password, username } = req.body;
        if (password === '888888' && username === 'admin')
        {
            return res.json({
                status: 'ok',
                code: 0,
                token: "sdfsdfsdfdsf",
                data: {
                    id: 1,
                    username: 'kenny',
                    sex: 6
                }
            });
        } else
        {
            return res.status(403).json({
                status: 'error',
                code: 403
            });
        }
    },
}

文件導出一個對象,key值是’POST /api/login/account’這樣的格式,前面是 請求方式+空格+path,GET方法可以省略掉請求方式
value值可以直接是一個json 也可以是一個回調函數(req,res)=>{},寫法和express的寫法一致

注意,一般不在index.js中具體的接口,index.js是聚合導出所有的接口

配置mock/index.js

如果你不想在項目中引入過多的依賴,可以選擇不使用mocker-api這個庫,自己來實現不不同api的響應

不使用mocker-api

在craco.config.js的配置文件中,本質上爲devServer的before屬性,定義一個function,進行請求路徑的匹配,但是顯然,我們不可能在配置文件中編寫返回數據的代碼

/* craco.config.js */
const path = require('path')

module.exports = {
    devServer: {
    	//如果不使用mocker-api庫
   		before: function (app)
        {
        	//我們不應該在配置文件中編寫數據返回
            app.get('/api/user/1', (req, res) =>
            {
                res.json({ id: 123, name: 456 })
            });
            app.get('/api/login', (req, res) =>
            {
                res.json({status:1,msg:"xxxxx"})
            });
        },
};

因此將配置文件craco.config.js改成如下代碼

/* craco.config.js */
const path = require('path')

module.exports = {
    devServer: {
        before: require('./mock/index')
    }
};

然後我們在mock/index.js中導出一個function(app),裏面包含所有的路徑響應
mock/index.js文件內容如下

const fs = require('fs')
const path = require('path')

let mockData={}
//通過遞歸函數,遍歷所有mock文件夾下的文件,彙總所有的路徑和響應
/*形成一個如下形式的對象mockData
{
  'POST /api/login/account': (req, res) =>{res.json({ id: 123, name: 456 })}),
  '/api/user': { id: 1, username: 'kenny', sex: 6 },
  'GET /api/user/list': [
    { id: 1, username: 'kenny', sex: 6 },
    { id: 2, username: 'kenny', sex: 6 }
  ],
  'DELETE /api/user/:id': (req, res) =>{res.json({status:1,msg:"xxxxx"})}),
}
key值爲請求方法和路徑,value值是json數據或者響應回調函數
*/
function readMockDir(dir)
{
    let dirs = fs.readdirSync(dir)
   
    dirs.forEach(file =>
    {
        let _path = path.join(dir, file)
        let isDirectory = fs.statSync(_path).isDirectory()
        if (isDirectory) {
            readMockDir(_path)
        }
        else
        {
            Object.assign(mockData,require(_path))
        }
    })
}
readMockDir(path.join(__dirname))

//導出一個如下形式的函數
//function(app){ 
//	app.get('path',(req,res)=>{})
//	app.get('path',(req,res)=>{})
//	app.get('path',(req,res)=>{})
//  ...
//  ...
//} 
module.exports = function (app)
{
	//遍歷上一步代碼中生產的mockData對象,將每一個鍵值對轉換成app.get('path',(req,res)=>{})這樣的形式
    for (let key in mockData)
    {
    	//解析請求方法和路徑
        let _key = key.replace(/(^\s*)|(\s*$)/g, '')
        //console.log(_key);
        let _method = 'get'
        let _url = _key.replace(/^(get|post|put|delete)\s*/i, function (rs, $1)
        {
            _method = $1.toLowerCase();
            return '';
        })
		
		//解析響應是json形式,還是回調形式
		//如果是json形式的,就拼裝一個(req,res)=>{}回調函數,並設置一個隨機的延遲時間,來模擬網絡延遲
        if (typeof mockData[key] !='function') {
            app[_method](_url, (req, res) =>
            {
                let timeout = (Math.random() * 2800) + 200
                //console.log(timeout);
                setTimeout(() => {
                    res.json(mockData[key])
                }, timeout);
                
            })
        }
        //如果響應已經是回調函數的形式了
        else
        {
            app[_method](_url, mockData[key])
        }
        
    }
}

這樣我們就可以在mock文件夾下任意文件中編寫接口,模擬後端的數據

使用mocker-api

當然也可以使用 mocker-api 庫,來稍微簡化一下上面的代碼
首先安裝mocker-api

npm install --save-dev mocker-api

在craco.config.js文件中輸入以下內容

/* craco.config.js */
const apiMocker = require('mocker-api')//使用mocker-api庫
const path = require('path')

module.exports = {
    devServer: {
		//如果使用mocker-api庫
        before(app)
        {
            apiMocker(app, path.resolve('./mock/index.js'), {
                
            })
        }
    }
};

其實還是配置devServer.before的屬性,只是apiMocker()函數幫我們做了解析 請求方法,路徑和響應的工作
path.resolve(’./mock/index.js’)這個語句,同樣也是導入mock/index.js 文件,只是現在有了mocker-api的幫助,我們只需要導入一個形式如下的對象

{
  _proxy: {},
  'POST /api/login/account': [Function: POST /api/login/account],
  'GET /api/:owner/:repo/raw/:ref/(.*)': [Function: GET /api/:owner/:repo/raw/:ref/(.*)],
  '/api/user': { id: 1, username: 'kenny', sex: 6 },
  'GET /api/user/list': [
    { id: 1, username: 'kenny', sex: 6 },
    { id: 2, username: 'kenny', sex: 6 }
  ],
  'DELETE /api/user/:id': [Function: DELETE /api/user/:id]
}

同樣也是一個json對象,key值代表請求方式和路徑,value表示響應,可以是json 也可以是回調函數,其中
_proxy: {},這個對象可以進行一些代理服務器的設置,具體請參考mocker-api的文檔
鏈接:https://www.npmjs.com/package/mocker-api

mock/index.js的功能就是導出一個上面這樣形式的對象
mock/index.js

const fs = require('fs')
const path = require('path')

let mockData={}
//通過遞歸函數,遍歷所有mock文件夾下的文件,彙總所有的路徑和響應
/*形成一個如下形式的對象mockData
{
  'POST /api/login/account': (req, res) =>{res.json({ id: 123, name: 456 })}),
  '/api/user': { id: 1, username: 'kenny', sex: 6 },
  'GET /api/user/list': [
    { id: 1, username: 'kenny', sex: 6 },
    { id: 2, username: 'kenny', sex: 6 }
  ],
  'DELETE /api/user/:id': (req, res) =>{res.json({status:1,msg:"xxxxx"})}),
}
key值爲請求方法和路徑,value值是json數據或者響應回調函數
*/
function readMockDir(dir)
{
    let dirs = fs.readdirSync(dir)
   
    dirs.forEach(file =>
    {
        let _path = path.join(dir, file)
        let isDirectory = fs.statSync(_path).isDirectory()
        if (isDirectory) {
            readMockDir(_path)
        }
        else
        {
            Object.assign(mockData,require(_path))
        }
    })
}
readMockDir(path.join(__dirname))

//可以在這裏對mocker-api做一些代理服務器等等的配置
const proxy = {
    // Priority processing.
    // apiMocker(app, path, option)
    // This is the option parameter setting for apiMocker
    _proxy: {
        // proxy: {
        //     // Turn a path string such as `/user/:name` into a regular expression.
        //     // https://www.npmjs.com/package/path-to-regexp
        //     // '/repos/(.*)': 'https://api.github.com/',
        // },
        // // rewrite target's url path. Object-keys will be used as RegExp to match paths.
        // // https://github.com/jaywcjlove/mocker-api/issues/62
        // pathRewrite: {
        //     '^/api/repos/': '/repos/',
        // },
        // changeHost: true,
        // // modify the http-proxy options
        // httpProxy: {
        //     options: {
        //         ignorePath: true,
        //     },
        //     listeners: {
        //         proxyReq: function (proxyReq, req, res, options)
        //         {
        //             console.log('proxyReq');
        //         },
        //     },
        // },
    },
}

//合併對象
Object.assign(proxy, mockData)
//console.log(proxy);

//爲總體的響應設置一個延遲來模擬網絡延遲
const delay = require('mocker-api/lib/delay');
const noProxy = process.env.NO_PROXY === 'true';

//但是這裏的延遲比較尷尬,即使你採用的隨機數,也只是在你引入index時產生一次,比如1500ms
//然後你所有的api請求都會是這個固定數值延遲,並不是每個請求單獨生產隨機延遲
//let delayTime=(Math.random()*1800)+200)
let delayTime=1000
module.exports = (noProxy ? {} : delay(proxy, delayTime);

關於模擬延遲的問題,可以修改mocker-api的源碼
node_modules/mocker-api/lib/delay.js 這個文件
在這裏插入圖片描述
直接在 setTimeout 中,把timer改成隨機數應該就可以了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章