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改成随机数应该就可以了

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