关于前端:前端mock完美解决方案实战

48次阅读

共计 17670 个字符,预计需要花费 45 分钟才能阅读完成。

写在后面,本文浏览须要肯定 Nodejs 的相干常识,因为会扩大 webpack 的相干性能,并且实现须要恪守肯定约定和 Ajax 封装。积淀的脚手架也放到 Github 上供应同学参考 React-Starter,使用手册还没写欠缺,整体思路和 React 还是 Vue 无关,如果对大家有播种记得 Star 下。
它有这些性能:

  • 开发打包有不同配置
  • eslint 验证
  • 代码格调对立
  • commit 标准验证
  • 接口 mock
  • 热更新
  • 异步组件

Mock 性能介绍

市面上讲前端 mock 怎么做的文章很多,整体上浏览下来的没有一个真正站在前端角度上让我感觉弱小和易用的。上面就说下我冀望的前端 mock 要有哪些性能:

  1. mock 性能和前端代码解耦
  2. 一个接口反对多种 mock 状况
  3. 无需依赖另外的后端服务和第三方库
  4. 能在 network 看到 mock 接口的申请且能辨别
  5. mock 数据、接口配置和页面在同一个目录下
  6. mock 配置扭转无需重启前端 dev
  7. 生产打包能够把 mock 数据注入到打包的 js 中走前端 mock
  8. 对于后端已有的接口也能疾速把 Response 数据转化为 mock 数据

下面的这些性能我讲其中几点的作用:

对于第 7 点的作用是后续我的项目开发实现,在齐全没有开发后端服务的状况下,也能够进行演示。这对于一些 ToB 定制的我的项目来积淀我的项目地图(案例)很有作用。
对于第 8 点在开发环境后端服务常常不稳固下,不依赖后端也能做页面开发,外围是能实现一键生成 mock 数据。

配置解耦

耦合状况

什么是前端配置解耦,首先让咱们看下平时配置耦合状况有哪些:

  • webpack-dev 后端测试环境变了须要改 git 跟踪的代码
  • dev 和 build 的时候 须要改 git 跟踪的代码
  • 开发的时候想这个接口 mock 须要改 git 跟踪的代码 mockUrl ,mock?

如何解决

前端依赖的配置解耦的思路是配置文件 conf.json 是在 dev 或 build 的时候动静生成的,而后该文件在前端我的项目援用:

├── config
│   ├── conf.json                                    # git 不跟踪
│   ├── config.js                                    # git 不跟踪
│   ├── config_default.js
│   ├── index.js
│   └── webpack.config.js
├── jsconfig.json
├── mock.json                                            # git 不跟踪

webpack 配置文件引入 js 的配置,生成 conf.json

// config/index.js
const _ = require("lodash");
let config = _.cloneDeep(require("./config_default"))
try {const envConfig = require('./config') // eslint-disable-line
  config = _.merge(config, envConfig);
} catch (e) {//}
module.exports = config;

默认应用 config_default.js 的内容,如果有 config.js 则笼罩,开发的时候复制 config_default.js 为 config.js 后续相干配置能够批改 config.js 即可。

// config/config_default.js
const pkg = require("../package.json");
module.exports = {
  projectName: pkg.name,
  version: pkg.version,
  port: 8888,
  proxy: {
    "/render-server/api/*": {
      target: `http://192.168.1.8:8888`,
      changeOrigin: true, // 反对跨域申请
      secure: true, // 反对 https
    },
  },
  ...
  conf: {
    dev: {
      title: "前端模板",
      pathPrefix: "/react-starter", // 对立前端门路前缀
      apiPrefix: "/api/react-starter", //
      debug: true,
      delay: 500,    // mock 数据模仿提早
      mock: {
        // "global.login": "success",
        // "global.loginInfo": "success",
      }
    },
    build: {
      title: "前端模板",
      pathPrefix: "/react-starter",
      apiPrefix: "/api/react-starter",
      debug: false,
      mock: {}}
  }
};

在开发或打包的时候依据环境变量应用 conf.dev 或 conf.build 生成 conf.json 文件内容

// package.json
{
  "name": "react-starter",
  "version": "1.0.0",
  "description": "react 前端开发脚手架",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server --config'./config/webpack.config.js'--open --mode development",
    "build": "cross-env BUILD_ENV=VERSION webpack --config'./config/webpack.config.js'--mode production --progress --display-modules && npm run tar",
    "build-mock": "node ./scripts/build-mock.js"
  },
  ...
}

指定 webpack 门路是./config/webpack.config.js

而后在 webpack.config.js 中引入配置并生成 conf.json 文件

// config/webpack.config.js
const config = require('.')
const env = process.env.BUILD_ENV ? 'build' : 'dev'
const confJson = env === 'build' ? config.conf.build : config.conf.dev
fs.writeFileSync(path.join(__dirname, './conf.json'),  JSON.stringify(confGlobal, null, '\t'))

援用配置

src/common/utils.jsx 文件中暴露出配置项,配置也能够通过 window.conf 来笼罩

// src/common/utils.jsx
import conf from '@/config/conf.json'
export const config = Object.assign(conf, window.conf)

而后就能够在各个页面中应用

import {config} from '@src/common/utils'
class App extends Component {render() {
    return (<Router history={history}>
        <Switch>
          <Route path={`${config.pathPrefix}`} component={Home} />
          <Redirect from="/" to={`${config.pathPrefix}`} />
        </Switch>
      </Router>
    )
  }
}
ReactDOM.render(
     <App />,
  document.getElementById('root'),
)

Mock 实现

成果

为了实现咱们想要的 mock 的相干性能,首先是否开启 mock 的配置解耦能够通过下面说的形式来实现,咱们个别在页面异步申请的时候都会目录定义一个 io.js 的文件, 外面定义了以后页面须要调用的相干后端接口:

// src/pages/login/login-io.js
import {createIo} from '@src/io'

const apis = {
  // 登录
  login: {
    method: 'POST',
    url: '/dtwave-boot/sys/login',
  },
  // 登出
  logout: {
    method: 'POST',
    url: '/dtwave-boot/sys/logout',
  },
}
export default createIo(apis, 'login') // 对应 login-mock.json

下面定义了登录和登出接口,咱们心愿对应开启的 mock 申请能应用当前目录下的 login-mock.json 文件的内容

// src/pages/login/login-mock.json
{
    "login": {
        "failed": {
            "success": false,
            "code": "ERROR_PASS_ERROR",
            "content": null,
            "message": "账号或明码谬误!"
        },
        "success": {
            "success": true,
            "code": 0,
            "content": {
                "name": "admin",
                "nickname": "超级管理员",
                "permission": 15
            },
            "message": ""
        }
    },
    "logout": {
        "success": {
            "success": true,
            "code": 0,
            "content": null,
            "message": ""
        }
    }
}

在调用 logout 登出这个 Ajax 申请的时候且咱们的 conf.json 中配置的是 "login.logout": "success" 就返回login-mock.json 中的 login.success 的内容,配置没有匹配到就申请转发到后端服务。

// config/conf.json
{
    "title": "前端后盾模板",
    "pathPrefix": "/react-starter",
    "apiPrefix": "/api/react-starter",
    "debug": true,
    "delay": 500,
    "mock": {"login.logout": "success"}
}

这是咱们最终要实现的成果,这里有一个约定:我的项目目录下所有以 -mock.jsom 文件结尾的文件为 mock 文件,且文件名不能反复

思路

在 webpack 配置项中 devServer 的 proxy 配置接口的转发设置,接口转发应用了功能强大的 http-proxy-middleware 软件包,咱们约定 proxy 的配置格局是:

  proxy: {
    "/api/react-starter/*": {
      target: `http://192.168.90.68:8888`,
      changeOrigin: true,
      secure: true,
      // onError: (),
      // onProxyRes,
      // onProxyReq  
    },
  },

它有几个事件触发的配置:

  • option.onError 呈现谬误
  • option.onProxyRes 后端响应后
  • option.onProxyReq 申请转发前
  • option.onProxyReqWs
  • option.onOpen
  • option.onClose

所以咱们须要定制这几个事件的解决,次要是申请转发前和申请解决后

onProxyReq

想在这里来实现 mock 的解决,如果匹配到了 mock 数据咱们就间接响应,就不转发申请到后端。怎么做呢:思路是依赖申请头,dev 状况下前端在调用的时候是否注入约定好的申请头 通知我须要寻找哪个 mock 数据项,咱们约定 Header:

  • mock-key 来匹配 mock 文件如 login-mock.json 的内容,如login
  • mock-method 来匹配对应文件内容的办法项 如logout

而后 conf.json 中 mock 配置寻找到具体的响应我的项目如:"login.logout": "success/failed"的内容

onProxyRes

如果调用了实在的后端申请,就把申请的响应数据缓存下来,缓存到 api-cache 目录下文件格式mock-key.mock-method.json

├── api-cache                                    # git 不跟踪
│   ├── login.login.json
│   └── login.logout.json
// api-cache/global.logout.json
{
    "success": {
        "date": "2020-11-17 05:32:17",
        "method": "POST",
        "path": "/render-server/api/logout",
        "url": "/render-server/api/logout",
        "resHeader": {
            "content-type": "application/json; charset=utf-8",
      ...
        },
        "reqHeader": {
            "host": "127.0.0.1:8888",
            "mock-key": "login",
            "mock-method": "logout"
      ...
        },
        "query": {},
        "reqBody": {},
        "resBody": {
            "success": true,
            "code": 0,
            "content": null,
            "message": ""
        }
    }
}

这样做的目标是为了后续实现一键生成 mock 文件。

前端接口封装

应用

下面咱们看到定义了接口的 io 配置:

// src/pages/login/login-io.js
import {createIo} from '@src/io'

const apis = {
  // 登录
  login: {
    method: 'POST',
    url: '/dtwave-boot/sys/login',
  },
  // 登出
  logout: {
    method: 'POST',
    url: '/dtwave-boot/sys/logout',
  },
}
export default createIo(apis, 'login') // login 注册到 header 的 mock-key

咱们在 store 中应用

// src/pages/login/login-store.js

import {observable, action, runInAction} from 'mobx'
import io from './login-io'
// import {config, log} from './utils'

export class LoginStore {
  // 用户信息
  @observable userInfo
  // 登陆操作
  @action.bound
  async login(params) {const {success, content} = await io.login(params)
    if (!success) return
    runInAction(() => {this.userInfo = content})
  }
}
export default LoginStore

通过 createIo(apis, 'login') 的封装在调用的时候就能够非常简单的来传递申请参数,简略模式下会判断参数是到 body 还是到 query 中。简单的也能够反对比方能够 header,query, body 等这里不演示了。

createIo 申请封装

这个是前端接口封装的要害中央,也是 mock 申请头注入的中央

// src/io/index.jsx
import {message, Modal} from 'antd'
import {config, log, history} from '@src/common/utils'
import {ERROR_CODE} from '@src/common/constant'
import creatRequest from '@src/common/request'
let mockData = {}
try {
  // eslint-disable-next-line global-require, import/no-unresolved
  mockData = require('@/mock.json')
} catch (e) {log(e)
}

let reloginFlag = false
// 创立一个 request
export const request = creatRequest({
  // 自定义的申请头
  headers: {'Content-Type': 'application/json'},
  // 配置默认返回数据处理
  action: (data) => {
    // 对立解决未登录的弹框
    if (data.success === false && data.code === ERROR_CODE.UN_LOGIN && !reloginFlag) {
      reloginFlag = true
      // TODO 这里可能对立跳转到 也能够是弹窗点击跳转
      Modal.confirm({
        title: '从新登录',
        content: '',
        onOk: () => {// location.reload()
          history.push(`${config.pathPrefix}/login?redirect=${window.location.pathname}${window.location.search}`)
          reloginFlag = false
        },
      })
    }
  },
  // 是否谬误显示 message
  showError: true,
  message,
  // 是否以抛出异样的形式 默认 false {success: boolean 判断}
  throwError: false,
  // mock 数据申请的等待时间
  delay: config.delay,
  // 日志打印
  log,
})

// 标识是否是简略传参数,值为 true 标识简单封装
export const rejectToData = Symbol('flag')

/**
 * 创立申请 IO 的封装
 * @param ioContent {any { url: string method?: string mock?: any apiPrefix?: string}}
  }
 * @param name mock 数据的对应文件去除 -mock.json 后的
 */
export const createIo = (ioContent, name = '') => {const content = {}
  Object.keys(ioContent).forEach((key) => {
    /**
     * @param {baseURL?: string, rejectToData?: boolean,  params?: {}, query?: {}, timeout?: number, action?(data: any): any, headers?: {},  body?: any, data?: any,   mock?: any}
     * @returns {message, content, code,success: boolean}
     */
    content[key] = async (options = {}) => {
      // 这里判断简略申请封装 rejectToData=true 示意简单封装
      if (!options[rejectToData]) {
        options = {data: options,}
      }
      delete options[rejectToData]
      if (
        config.debug === false &&
        name &&
        config.mock &&
        config.mock[`${name}.${key}`] &&
        mockData[name] &&
        mockData[name][key]
      ) { // 判断是否是生产打包 mock 注入到代码中
        ioContent[key].mock = JSON.parse(JSON.stringify(mockData[name][key][config.mock[`${name}.${key}`]]))
      } else if (name && config.debug === true) { // 注入 mock 申请头
        if (options.headers) {options.headers['mock-key'] = name
          options.headers['mock-method'] = key
        } else {options.headers = {'mock-key': name, 'mock-method': key}
        }
      }
      const option = {...ioContent[key], ...options}

      option.url = ((option.apiPrefix ? option.apiPrefix : config.apiPrefix) || '') + option.url

      return request(option)
    }
  })
  return content
}

这里对 request 也做进一步的封装,配置项设置了一些默认的解决设置。比方通用的申请响应失败的是否有一个 message,未登录的状况是否有一个弹窗提醒点击跳转登陆页。如果你想定义多个通用解决能够再创立一个 request2 和 createIo2。

request 封装 axios

是基于 axios 的二次封装,并不是十分通用,次要是在约定的申请失败和胜利的解决有定制,如果须要能够本人批改应用。

import axios from 'axios'

// 配置接口参数
// declare interface Options {
//   url: string
//   baseURL?: string
//   // 默认 GET
//   method?: Method
//   // 标识是否注入到 data 参数
//   rejectToData?: boolean
//   // 是否间接弹出 message 默认是
//   showError?: boolean
//   // 指定 回调操作 默认登录解决
//   action?(data: any): any
//   headers?: {//     [index: string]: string
//   }
//   timeout?: number
//   // 指定路由参数
//   params?: {//     [index: string]: string
//   }
//   // 指定 url 参数
//   query?: any
//   // 指定 body 参数
//   body?: any
//   // 混合解决 Get 到 url, delete post 到 body, 也替换路由参数 在 createIo 封装
//   data?: any
//   mock?: any
// }
// ajax 申请的对立封装
// TODO 1. 对 jsonp 申请的封装 2. 反复申请

/**
 * 返回 ajax 申请的对立封装
 * @param Object option 申请配置
 * @param {boolean} opts.showError 是否谬误调用 message 的 error 办法
 * @param {object} opts.message  蕴含 .error 办法 showError true 的时候调用
 * @param {boolean} opts.throwError 是否出错抛出异样
 * @param {function} opts.action  蕴含 自定义默认解决 比方未登录的解决
 * @param {object} opts.headers  申请头默认 content-type: application/json
 * @param {number} opts.timeout  超时 默认 60 秒
 * @param {number} opts.delay   mock 申请提早
 * @returns {function} {params, url, headers, query, data, mock} data 混合解决 Get 到 url, delete post 到 body, 也替换路由参数 在 createIo 封装
 */
export default function request(option = {}) {return async (optionData) => {
    const options = {
      url: '',
      method: 'GET',
      showError: option.showError !== false,
      timeout: option.timeout || 60 * 1000,
      action: option.action,
      ...optionData,
      headers: {'X-Requested-With': 'XMLHttpRequest', ...option.headers, ...optionData.headers},
    }
    // 简略申请解决
    if (options.data) {if (typeof options.data === 'object') {Object.keys(options.data).forEach((key) => {if (key[0] === ':' && options.data) {options.url = options.url.replace(key, encodeURIComponent(options.data[key]))
            delete options.data[key]
          }
        })
      }
      if ((options.method || '').toLowerCase() ==='get'|| (options.method ||'').toLowerCase() === 'head') {options.query = Object.assign(options.data, options.query)
      } else {options.body = Object.assign(options.data, options.body)
      }
    }
    // 路由参数解决
    if (typeof options.params === 'object') {Object.keys(options.params).forEach((key) => {if (key[0] === ':' && options.params) {options.url = options.url.replace(key, encodeURIComponent(options.params[key]))
        }
      })
    }
    // query 参数解决
    if (options.query) {const paramsArray = []
      Object.keys(options.query).forEach((key) => {if (options.query[key] !== undefined) {paramsArray.push(`${key}=${encodeURIComponent(options.query[key])}`)
        }
      })
      if (paramsArray.length > 0 && options.url.search(/\?/) === -1) {options.url += `?${paramsArray.join('&')}`
      } else if (paramsArray.length > 0) {options.url += `&${paramsArray.join('&')}`
      }
    }
    if (option.log) {option.log('request  options', options.method, options.url)
      option.log(options)
    }
    if (options.headers['Content-Type'] === 'application/json' && options.body && typeof options.body !== 'string') {options.body = JSON.stringify(options.body)
    }
    let retData = {success: false}
    // mock 解决
    if (options.mock) {retData = await new Promise((resolve) =>
        setTimeout(() => {resolve(options.mock)
        }, option.delay || 500),
      )
    } else {
      try {
        const opts = {
          url: options.url,
          baseURL: options.baseURL,
          params: options.params,
          method: options.method,
          headers: options.headers,
          data: options.body,
          timeout: options.timeout,
        }
        const {data} = await axios(opts)
        retData = data
      } catch (err) {
        retData.success = false
        retData.message = err.message
        if (err.response) {
          retData.status = err.response.status
          retData.content = err.response.data
          retData.message = ` 浏览器申请非正常返回: 状态码 ${retData.status}`
        }
      }
    }

    // 主动处理错误音讯
    if (options.showError && retData.success === false && retData.message && option.message) {option.message.error(retData.message)
    }
    // 解决 Action
    if (options.action) {options.action(retData)
    }
    if (option.log && options.mock) {option.log('request response:', JSON.stringify(retData))
    }
    if (option.throwError && !retData.success) {const err = new Error(retData.message)
      err.code = retData.code
      err.content = retData.content
      err.status = retData.status
      throw err
    }
    return retData
  }
}
一键生成 mock

依据 api-cache 下的接口缓存和定义的 xxx-mock.json 文件来生成。

# "build-mock": "node ./scripts/build-mock.js"
# 所有:npm run build-mock mockAll 
# 单个 mock 文件:npm run build-mock login
# 单个 mock 接口:npm run build-mock login.logout
# 简单 
npm run build-mock login.logout user

具体代码参考 build-mock.js

mock.json 文件生成

为了在 build 打包的时候把 mock 数据注入到前端代码中去,使得 mock.json 文件内容尽可能的小,会依据 conf.json 的配置项来动静生成 mock.json 的内容,如果 build 外面没有开启 mock 项,内容就会是一个空 json 数据。当然后端接口代理解决内存中也映射了一份该 mock.json 的内容。这里须要做几个事件:

  • 依据配置动静生成 mock.json 的内容
  • 监听 config 文件夹 判断对于 mock 的配置项是否有扭转从新生成 mock.json
// scripts/webpack-init.js 在 wenpack 配置文件中初始化
const path = require('path')
const fs = require('fs')
const {syncWalkDir} = require('./util')
let confGlobal = {}
let mockJsonData = {}
exports.getConf = () => confGlobal
exports.getMockJson =() => mockJsonData

/**
 * 初始化我的项目的配置 动静生成 mock.json 和 config/conf.json
 * @param {string} env  dev|build
 */
 exports.init = (env = process.env.BUILD_ENV ? 'build' : 'dev') => {delete require.cache[require.resolve('../config')]
  const config  = require('../config')
  const confJson = env === 'build' ? config.conf.build : config.conf.dev
  confGlobal = confJson
  // 1. 依据环境变量来生成
  fs.writeFileSync(path.join(__dirname, '../config/conf.json'),  JSON.stringify(confGlobal, null, '\t'))
  buildMock(confJson)
 }
 
 // 生成 mock 文件数据
 const buildMock = (conf) => {
  // 2. 动静生成 mock 数据 读取 src 文件夹上面所有以 -mock.json 结尾的文件 存储到 io/index.json 文件当中
  let mockJson = {}
  const mockFiles = syncWalkDir(path.join(__dirname, '../src'), (file) => /-mock.json$/.test(file))
  console.log('build mocks: ->>>>>>>>>>>>>>>>>>>>>>>')
  mockFiles.forEach((filePath) => {const p = path.parse(filePath)
    const mockKey = p.name.substr(0, p.name.length - 5)
    console.log(mockKey, filePath)
    if (mockJson[mockKey]) {console.error(` 有雷同的 mock 文件名称 ${p.name} 存在 `, filePath)
    }
    delete require.cache[require.resolve(filePath)]
    mockJson[mockKey] = require(filePath)
  })
  // 如果是打包环境,最小化 mock 资源数据
  const mockMap = conf.mock || {}
  const buildMockJson = {}
  Object.keys(mockMap).forEach((key) => {const [name, method] = key.split('.')
    if (mockJson[name][method] && mockJson[name][method][mockMap[key]]) {if (!buildMockJson[name]) buildMockJson[name] = {}
      if (!buildMockJson[name][method]) buildMockJson[name][method] = {}
      buildMockJson[name][method][mockMap[key]] = mockJson[name][method][mockMap[key]]
    }
  })
  mockJsonData = buildMockJson
  fs.writeFileSync(path.join(__dirname, '../mock.json'), JSON.stringify(buildMockJson, null, '\t'))
 }
 
 // 监听配置文件目录下的 config.js 和 config_default.js
const confPath = path.join(__dirname, '../config')

if ((env = process.env.BUILD_ENV ? 'build' : 'dev') === 'dev') {fs.watch(confPath, async (event, filename) => {if (filename === 'config.js' || filename === 'config_default.js') {delete require.cache[path.join(confPath, filename)]
      delete require.cache[require.resolve('../config')]
      const config  = require('../config')
      // console.log('config', JSON.stringify(config))
      const env = process.env.BUILD_ENV ? 'build' : 'dev'
      const confJson = env === 'build' ? config.conf.build : config.conf.dev
      if (JSON.stringify(confJson) !== JSON.stringify(confGlobal)) {this.init()
      }
    }
  });
}

接口代理解决

onProxyReq 和 onProxyRes

实现下面思路外面说的 onProxyReq 和 onProxyRes 响应解决

util.js

// scripts/api-proxy-cache 
const fs = require('fs')
const path = require('path')
const moment = require('moment')
const {getConf, getMockJson} = require('./webpack-init')
const API_CACHE_DIR = path.join(__dirname, '../api-cache')
const {jsonParse, getBody} = require('./util')

fs.mkdirSync(API_CACHE_DIR,{recursive: true})

module.exports = {
  // 代理前解决
  onProxyReq: async (_, req, res) => {req.reqBody = await getBody(req)
    const {'mock-method': mockMethod, 'mock-key': mockKey} = req.headers
    // eslint-disable-next-line no-console
    console.log(`Ajax 申请: ${mockKey}.${mockMethod}`,req.method, req.url)
    // eslint-disable-next-line no-console
    req.reqBody && console.log(JSON.stringify(req.reqBody, null, '\t'))
    if (mockKey && mockMethod) {
      req.mockKey = mockKey
      req.mockMethod = mockMethod
      const conf = getConf()
      const mockJson = getMockJson()
      if (conf.mock && conf.mock[`${mockKey}.${mockMethod}`] && mockJson[mockKey] && mockJson[mockKey][mockMethod]) {
        // eslint-disable-next-line no-console
        console.log(`use mock data ${mockKey}.${mockMethod}:`, conf.mock[`${mockKey}.${mockMethod}`], 'color: green')
        res.mock = true
        res.append('isMock','yes')
        res.send(mockJson[mockKey][mockMethod][conf.mock[`${mockKey}.${mockMethod}`]])
      }
     
    }
  },
  // 响应缓存接口
  onProxyRes: async (res, req) => {const {method, url, query, path: reqPath, mockKey, mockMethod} = req
    
    if (mockKey && mockMethod && res.statusCode === 200) {let resBody = await getBody(res)
      resBody = jsonParse(resBody)
      const filePath = path.join(API_CACHE_DIR, `${mockKey}.${mockMethod}.json`)
      let  data = {}
      if (fs.existsSync(filePath)) {data = jsonParse(fs.readFileSync(filePath).toString())
      }
      const cacheObj = {date: moment().format('YYYY-MM-DD hh:mm:ss'),
        method,
        path: reqPath,
        url,
        resHeader: res.headers,
        reqHeader: req.headers,
        query,
        reqBody: await jsonParse(req.reqBody),
        resBody: resBody
      }
      if (resBody.success === false) {data.failed = cacheObj} else {data.success = cacheObj}
      // eslint-disable-next-line no-console
      fs.writeFile(filePath, JSON.stringify(data,'','\t'), (err) => {err && console.log('writeFile', err)})
    }
  },
  // 后端服务没启的异样解决
  onError(err, req, res) {setTimeout(() => {if (!res.mock) {
       res.writeHead(500, {'Content-Type': 'text/plain',});
       res.end('Something went wrong. And we are reporting a custom error message.');
     }
   }, 10)
  }
}
webpack 配置

在 webpack 配置中引入应用

const config = require('.')
// config/webpack.config.js
const {init} = require('../scripts/webpack-init');
init();
// 接口申请本地缓存
const apiProxyCache = require('../scripts/api-proxy-cache')
for(let key in config.proxy) {config.proxy[key] = Object.assign(config.proxy[key], apiProxyCache);
}

const webpackConf = {
  devServer: {contentBase: path.join(__dirname, '..'), // 本地服务器所加载的页面所在的目录
    inline: true,
    port: config.port,
    publicPath: '/',
    historyApiFallback: {
      disableDotRule: true,
      // 指明哪些门路映射到哪个 html
      rewrites: config.rewrites,
    },
    host: '127.0.0.1',
    hot: true,
    proxy: config.proxy,
  },
}

总结

mock 做好其实在咱们前端理论中还是很有必要的,做过的我的项目如果后端被根除了想要回顾就能够应用 mock 让我的项目跑起来,能够寻找一些实现的成果来进行代码复用。以后介绍的 mock 流程实现有很多定制的开发,然而真正实现后,团队中的成员只是应用还是比较简单配置即可。

对于前端我的项目部署我也分享了一个 BFF 层,以后做的还不是很欠缺,也分享给大家参考

Render-Server 次要性能蕴含:

  • 一键部署 npm run deploy
  • 反对集群部署配置
  • 是一个文件服务
  • 是一个动态资源服务
  • 在线可视化部署前端我的项目
  • 配置热更新
  • 在线 Postman 及接口文档
  • 反对前端路由渲染,反对模板
  • 接口代理及门路替换
  • Web 平安反对 Ajax 申请验证,Referer 校验
  • 反对插件开发和在线配置 可实现:前端模板参数注入、申请头注入、IP 白名单、接口 mock、会话、第三方登陆等等

正文完
 0