As mentioned above, Nodejs is required for this article because it extends webPack’s functionality and the implementation is subject to conventions and Ajax encapsulation. React/Vue scaffolding is also posted on Github for students to refer to as react-Starter. The user manual has not been completed yet. The overall idea has nothing to do with React or Vue.

  • Development packaging has different configurations
  • Eslint validation
  • Uniform code style
  • Commit specification validation
  • Interface mock
  • Hot update
  • Asynchronous components

Mock Function Introduction

There are many articles on how to do front-end mock in the market, but none of them really makes me feel powerful and easy to use from the perspective of front-end. Here’s what I expect from a front-end mock:

  1. Mock functionality is decoupled from the front-end code
  2. An interface supports multiple mock cases
  3. No need to rely on additional back-end services and third-party libraries
  4. Mock interface requests can be seen on network and can be distinguished
  5. Mock data, interface configuration, and pages are in the same directory
  6. Mock configuration changes do not require a front-end dev restart
  7. Production packaging can be used to inject mock data into packaged JS to go front mock
  8. It can also quickly convert Response data to mock data for existing back-end interfaces

Here are some of the functions of the above functions:

The effect of point 7 is that the subsequent project development is complete and the demonstration can be done without developing back-end services at all. This is useful for some ToB custom projects to precipitate project maps (cases).

For point 8, in a development environment where backend services are often unstable, it is possible to do page development without relying on the backend. The core is the ability to generate mock data with one click.

The configuration of decoupling

Coupling conditions

What is front-end configuration decoupling? First, let’s take a look at the usual configuration coupling:

  • The Webpack-dev backend test environment has changed the code that needs to be changed for Git tracing
  • Git trace code needs to be changed for dev and build
  • You need to change the git trace mockUrl,mock?

How to solve

The idea of configuration decoupling for front-end dependencies is that the config/conf.json configuration file is dynamically generated at dev or build time and then referenced in the front-end project:

├─ config │ ├─ conf. Json# git does not trace│ ├ ─ ─ config. Js# git does not trace│ ├ ─ ─ config_default. Js │ ├ ─ ─ index. The js │ └ ─ ─ webpack. Config. Js ├ ─ ─ jsconfig. Json ├ ─ ─ the mock. Json# git does not trace
Copy the code

The webPack configuration file imports the JS configuration and generates 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;
Copy the code

By default, the content of config_default.js is used. If there is config.js, it will be overwritten.

// 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.// Support cross-domain requests
      secure: true./ / support HTTPS}},...conf: {
    dev: {
      title: "Front-end template".pathPrefix: "/react-starter".// Unified front-end path prefix
      apiPrefix: "/api/react-starter".//
      debug: true.delay: 500.// Mock data simulates latency
      mock: {
        // "global.login": "success",
        // "global.loginInfo": "success",}},build: {
      title: "Front-end template".pathPrefix: "/react-starter".apiPrefix: "/api/react-starter".debug: false.mock: {}}}};Copy the code

Conf. Json file content is generated at development or packaging time using conf.dev or conf.build based on environment variables

// package.json
{
  "name": "react-starter"."version": "1.0.0"."description": React Front-end development scaffolding."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 "},... }Copy the code

Webpack specified path is/config/webpack. Config. Js

You then import the configuration in webpack.config.js and generate the conf.json file

// 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'))
Copy the code

The reference configuration

The configuration items are exposed in the SRC /common/utils.jsx file, and the configuration can also be overridden through window.conf

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

It can then be used in individual pages

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'),Copy the code

The Mock implementation

The effect

In order to achieve the desired functionality of mock, we can first enable the configuration decoupling of mock by using the above method. We usually define an io.js file during asynchronous page requests, which defines the relevant back-end interface that the current page needs to call:

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

const apis = {
  / / login
  login: {
    method: 'POST'.url: '/auth/login',},/ / logout
  logout: {
    method: 'POST'.url: '/auth/logout',}}export default createIo(apis, 'login') / / the corresponding login - mock. Json
Copy the code

The login and logout interfaces are defined above, and we want the mock requests that are opened to use the contents of the login-mock.json file in the current directory

// src/pages/login/login-mock.json
{
	"login": {
		"failed": {
			"success": false."code": "ERROR_PASS_ERROR"."content": null."message": "Wrong account or password!"
		},
		"success": {
			"success": true."code": 0."content": {
				"name": "admin"."nickname": "Super Administrator"."permission": 15
			},
			"message": ""}},"logout": {
		"success": {
			"success": true."code": 0."content": null."message": ""}}}Copy the code

When logout is called to logout the Ajax request and our conf.json is configured with “login.logout”: “Success” returns the contents of login.success in login-mock.json, and if the configuration does not match, the request is forwarded to the back-end service.

// config/conf.json
{
	"title": "Front-end and back-end Templates"."pathPrefix": "/react-starter"."apiPrefix": "/api/react-starter"."debug": true."delay": 500."mock": {
		"login.logout": "success"}}Copy the code

This is what we’re going to end up with, with a convention that all files in the project directory that end in -mock.jsom are mock files, and the names cannot be repeated.

Train of thought

In the WebPack configuration item, The proxy of devServer configures the forwarding Settings of the interface. The interface forwarding uses the powerful HTTP-proxy-Middleware software package. We agree on the configuration format of proxy:

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

It has several event-triggered configurations:

  • Option. onError An error occurred

  • Option. onProxyRes after the back end responds

  • Option. onProxyReq Before the request is forwarded

  • option.onProxyReqWs

  • option.onOpen

  • option.onClose

So we need to customize the processing of these things, mainly before the request forward and after the request processing

onProxyReq

We want to implement mock processing here. If the mock data is matched, we respond directly without forwarding the request to the back end. If dev is called, can the front end inject the convention’s request Header to tell me which mock item to look for? We say Header:

  • mock-keyTo match mock files likelogin-mock.jsonSuch aslogin
  • mock-methodTo match the corresponding file content of the method items such aslogout

The mock configuration in conf.json then looks for specific response items such as “login.logout”: “success/failed”

onProxyRes

If a real back-end request is invoked, the request response data is cached in the api-cache file format mock-key.mock-method.json

├ ─ ─ API - cache# git does not trace│ ├─ ├─ login.login.json │ ├─ ├.login. login.jsonCopy the code
// api-cache/global.logout.json
{
	"success": {
		"date": "The 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": ""}}}Copy the code

This is done for subsequent implementation of one-click mock files.

Front-end interface encapsulation

use

Above we see the IO configuration that defines the interface:

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

const apis = {
  / / login
  login: {
    method: 'POST',
    url: '/auth/login',},/ / logout
  logout: {
    method: 'POST',
    url: '/auth/logout',
  },
}
export default createIo(apis, 'login') // login registers with the mock-key of the header
Copy the code

We use it in 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 {
  // User information
  @observable userInfo
  // Login operation
  @action.bound
  async login(params) {
    const {success, content} = await io.login(params)
    if(! success)return
    runInAction(() = > {
      this.userInfo = content
    })
  }
}
export default LoginStore
Copy the code

The createIo(apis, ‘login’) wrapper makes it very easy to pass the request parameters during the call. In simple mode, the parameters are checked whether they go to the body or query. You can support more complex things like header, Query, body and so on that I won’t show you.

CreateIo Request encapsulation

This is the key place where the front-end interface is encapsulated and where the mock request header is injected

// 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
// Create a request
export const request = creatRequest({
  // Custom request headers
  headers: {'Content-Type': 'application/json'},
  // The configuration returns data processing by default
  action: (data) = > {
    // Handle the unlogged dialog box
    if (data.success === false&& data.code === ERROR_CODE.UN_LOGIN && ! reloginFlag) { reloginFlag =true
      // TODO here can be a unified jump to or a popover click to jump to
      Modal.confirm({
        title: 'Log in again'.content: ' '.onOk: () = > {
          // location.reload()
          history.push(`${config.pathPrefix}/login? redirect=The ${window.location.pathname}The ${window.location.search}`)
          reloginFlag = false}}}}),// Whether message is displayed incorrectly
  showError: true,
  message,
  // Whether to throw an exception by default false {success: Boolean}
  throwError: false.// Wait time for mock data requests
  delay: config.delay,
  // Logs are printed
  log,
})

// Indicates whether simple transmission is possible. A value of true indicates complex encapsulation
export const rejectToData = Symbol('flag')

/** * Create a wrapper for the request IO *@param ioContent {any { url: string method? : string mock? : any apiPrefix? : string}} } *@param Name */ after -mock.json is removed from the corresponding file of mock data
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 indicates complex encapsulation
      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]
      ) { // Determine if the production package mock is injected into the code
        ioContent[key].mock = JSON.parse(JSON.stringify(mockData[name][key][config.mock[`${name}.${key}`]])}else if (name && config.debug === true) { // Inject the mock request header
        if (options.headers) {
          options.headers['mock-key'] = name
          options.headers['mock-method'] = key
        } else {
          options.headers = {'mock-key': name, 'mock-method': key}
        }
      }
      constoption = {... ioContent[key], ... options} option.url = ((option.apiPrefix ? option.apiPrefix : config.apiPrefix) ||' ') + option.url

      return request(option)
    }
  })
  return content
}
Copy the code

Request is further encapsulated here, with the configuration item setting some default processing Settings. For example, whether there is a message in response to a common request failure, whether there is a pop-up window prompting you to jump to the login page when you are not logged in. Create request2 and createIo2 if you want to define multiple generic processes.

The request to encapsulate axios

Axios-based secondary encapsulation is not very general, mainly in the convention of failed and successful processing custom, can be modified if necessary.

import axios from 'axios'

// Set interface parameters
// declare interface Options {
// url: string
// baseURL? : string
// // default GET
// method? : Method
// // identifies whether to inject the data parameter
// rejectToData? : boolean
// // Whether message is displayed The default value is Yes
// showError? : boolean
// // Specifies that the callback operation is handled by default login
// action? (data: any): any
// headers? : {
// [index: string]: string
/ /}
// timeout? : number
// // Specify route parameters
// params? : {
// [index: string]: string
/ /}
// // Specifies the URL parameters
// query? : any
// // Specifies the body argument
// body? : any
// // mixes Get to URL, delete post to body, and also replaces route parameters in createIo encapsulation
// data? : any
// mock? : any
// }
// Unified encapsulation of Ajax requests
// TODO 1. Encapsulation of JSONP requests 2. Repeated requests

/** * Returns a unified wrapper for the Ajax request *@param Object Option Request configuration *@param {boolean} Opts.showerror Whether message's error method * is incorrectly called@param {object} Call * when opts.message contains the. Error method showError true@param {boolean} Opts.throwerror Whether an error is thrown *@param {function} Opts.action contains custom default actions such as unlogged actions *@param {object} Opts. Headers Request Header Default Content-Type: application/json *@param {number} Opts. Timeout Indicates the timeout period. The default value is 60 seconds *@param {number} Opts.delay Mock Request delay *@returns {function} {params, url, headers, query, data, mock} Data mixes Get to URL, delete post to body, and also replaces route parameters in createIo encapsulation */
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}, }// Simple request processing
    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)
      }
    }
    // Route parameter processing
    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 parameter processing
    if (options.query) {
      const paramsArray = []
      Object.keys(options.query).forEach((key) = > {
        if(options.query[key] ! = =undefined) {
          paramsArray.push(`${key}=The ${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 && typeofoptions.body ! = ='string') {
      options.body = JSON.stringify(options.body)
    }
    let retData = {success: false}
    / / the mock processing
    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 = 'Browser request abnormal return: status code${retData.status}`}}}// Handle error messages automatically
    if (options.showError && retData.success === false && retData.message && option.message) {
      option.message.error(retData.message)
    }
    / / handle the 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
  }
}
Copy the code
One-click mock generation

It is generated from the interface cache under api-cache and the defined xxx-mock.json file.

# "build-mock": "node ./scripts/build-mock.js"
# all:
npm run build-mock mockAll 
# single mock file:
npm run build-mock login
# single mock interface:
npm run build-mock login.logout
# complex
npm run build-mock login.logout user
Copy the code

See build-mock.js for more details

Mock. Json file generation

In order to inject mock data into the front-end code at build packaging time to keep the mock.json file content as small as possible, the content of the mock. Of course, a copy of mock.json is also mapped into the backend interface proxy processing memory. Here are a few things to do:

  • Dynamically generate the contents of mock.json based on the configuration
  • Listen to the config folder to see if configuration items about mock have changed and regenerate mock.json
// scripts/webpack-init.js is initialized in the WENpack configuration file
const path = require('path')
const fs = require('fs')
const {syncWalkDir} = require('./util')
let confGlobal = {}
let mockJsonData = {}
exports.getConf = () = > confGlobal
exports.getMockJson =() = > mockJsonData

/** * Initializes the configuration of the project to dynamically generate mock.json and 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. Generate based on environment variables
  fs.writeFileSync(path.join(__dirname, '.. /config/conf.json'),  JSON.stringify(confGlobal, null.'\t'))
  buildMock(confJson)
 }
 
 // Generate mock file data
 const buildMock = (conf) = > {
  All files in the SRC folder that end in -mock.json are stored in the IO /index.json file
  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('has the same mock file name${p.name}There are `, filePath)
    }
    delete require.cache[require.resolve(filePath)]
    mockJson[mockKey] = require(filePath)
  })
  // If the environment is packaged, minimize the mock resource data
  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'))}// Listen on config.js and config_default.js in the config file directory
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()
      }
    }
  });
}
Copy the code

Interface proxy processing

OnProxyReq and onProxyRes

Implement onProxyReq and onProxyRes response processing

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 = {
  // proxy preprocessing
  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 request:${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}`]])}}},// Response caching interface
  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)})
    }
  },
  // The backend service is not started exception handling
  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)}}Copy the code
Webpack configuration

Introduced in the WebPack configuration

const config = require('. ')
// config/webpack.config.js
const {init} = require('.. /scripts/webpack-init');
init();
// The interface requests the local cache
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, '.. '), // The directory where the page is loaded by the local server
    inline: true.port: config.port,
    publicPath: '/'.historyApiFallback: {
      disableDotRule: true.// specify which paths map to which HTML
      rewrites: config.rewrites,
    },
    host: '127.0.0.1'.hot: true.proxy: config.proxy,
  },
}

Copy the code

conclusion

In fact, mock is very necessary in our front-end practice. If the back-end of the project is eliminated, we can use mock to make the project run, and we can find some implementation effects to reuse the code. The mock process implementation described so far had a lot of custom development, but once it was done, it was used and configured by members of the team.

I also share a BFF layer about the front-end project deployment, the current work is not very perfect, also share for your reference

The main functions of Render-Server include:

  • One-click deployment NPM run deploy
  • Cluster deployment is supported
  • Is a file service
  • Is a static resource service
  • Visualize deploying front-end projects online
  • Configuring hot Update
  • Online Postman and interface documentation
  • Support front-end route rendering, support templates
  • Interface proxy and path replacement
  • Web security supports Ajax request validation and Referer verification
  • Support for plug-in development and online configuration can be achieved: front-end template parameter injection, request header injection, IP whitelist, interface mock, session, third party login, and more