讯享网
-
开发打包有不同配置 -
eslint 验证 -
代码风格统一 -
commit 规范验证 -
接口mock -
热更新 -
异步组件
-
mock功能和前端代码解耦 -
一个接口支持多种mock情况 -
无需依赖另外的后端服务和第三方库 -
能在network看到mock接口的请求且能区分 -
mock数据、接口配置和页面在同一个目录下 -
mock配置改变无需重启前端dev -
生产打包可以把mock数据注入到打包的js中走前端mock -
对于后端已有的接口也能快速把Response数据转化为mock数据
对于第8点在开发环境后端服务经常不稳定下,不依赖后端也能做页面开发,核心是能实现一键生成mock数据。
耦合情况
-
webpack-dev后端测试环境变了需要改git跟踪的代码 -
dev和build的时候 需要改git跟踪的代码 -
开发的时候想这个接口mock 需要改git跟踪的代码 mockUrl ,mock?
如何解决
讯享网├── config│ ├── conf.json # git 不跟踪│ ├── config.js # git 不跟踪│ ├── config_default.js│ ├── index.js│ └── webpack.config.js├── jsconfig.json├── mock.json # git 不跟踪
讯享网
讯享网// config/index.jsconst _ = require(“lodash”);let config = _.cloneDeep(require(”https://mp.weixin..com/config_default”))try {const envConfig = require(’https://mp.weixin..com/config’) // eslint-disable-lineconfig = _.merge(config, envConfig);} catch (e) {//}module.exports = config;
讯享网// config/config_default.jsconst 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: {}}}};
讯享网// package.json{“name”: “react-starter”,“version”: “1.0.0”,“description”: “react前端开发脚手架”,“main”: “index.js”,“scripts”: {“start”: “webpack-dev-server –config ‘https://mp.weixin..com/config/webpack.config.js’ –open –mode development”,“build”: “cross-env BUILD_ENV=VERSION webpack –config ‘https://mp.weixin..com/config/webpack.config.js’ –mode production –progress –display-modules && npm run tar”,“build-mock”: “node https://mp.weixin..com/scripts/build-mock.js “},…}
讯享网// config/webpack.config.jsconst config = require(‘.’)const env = process.env.BUILD_ENV ? ‘build’ : ‘dev’const confJson = env === ‘build’ ? config.conf.build : config.conf.devfs.writeFileSync(path.join(dirname, ‘https://mp.weixin..com/conf.json’), JSON.stringify(confGlobal, null, ‘\t’))
引用配置
fix code-snippetjs”> line-index code-snippet js”>
讯享网js” data-lang=“javascript”>// src/common/utils.jsximport 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’),)
效果
讯享网// src/pages/login/login-io.jsimport {createIo} from ‘@src/io’const apis = {// 登录login: {method: ‘POST’,url: ‘/auth/login’,},// 登出logout: {method: ‘POST’,url: ‘/auth/logout’,},}export default createIo(apis, ‘login’) // 对应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”: “”}}}
讯享网// config/conf.json{“title”: “前端后台模板”,“pathPrefix”: “/react-starter”,“apiPrefix”: “/api/react-starter”,“debug”: true,“delay”: 500,“mock”: {“login.logout”: “success”}}
思路
讯享网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-key 来匹配mock文件如login-mock.json的内容, 如login
- mock-method 来匹配对应文件内容的方法项 如logout
onProxyRes
讯享网├── 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”: “”}}}
前端接口封装
使用
讯享网// src/pages/login/login-io.jsimport {createIo} from ‘@src/io’const apis = {// 登录login: {method: ‘POST’,url: ‘/auth/login’,},// 登出logout: {method: ‘POST’,url: ‘/auth/logout’,},}export default createIo(apis, ‘login’) // login注册到header的mock-key
讯享网// src/pages/login/login-store.jsimport {observable, action, runInAction} from ‘mobx’import io from ‘https://mp.weixin..com/login-io’// import {config, log} from ‘https://mp.weixin..com/utils’export class LoginStore {// 用户信息@observable userInfo// 登陆操作@action.boundasync login(params) {const {success, content} = await io.login(params)if (!success) returnrunInAction(() => {this.userInfo = content})}}export default LoginStore
createIo 请求封装
讯享网// src/io/index.jsximport {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-unresolvedmockData = require(‘@/mock.json’)} catch (e) {log(e)}let reloginFlag = false// 创建一个requestexport 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},})}},// 是否错误显示messageshowError: 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’] = nameoptions.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.urlreturn request(option)}})return content}
request封装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 = falseretData.message = err.messageif (err.response) {retData.status = err.response.statusretData.content = err.response.dataretData.message =浏览器请求非正常返回: 状态码 ${retData.status}}}}// 自动处理错误消息if (options.showError && retData.success === false && retData.message && option.message) {option.message.error(retData.message)}// 处理Actionif (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.codeerr.content = retData.contenterr.status = retData.statusthrow err}return retData}}
一键生成mock
讯享网# “build-mock”: “node https://mp.weixin..com/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
mock.json文件生成
-
根据配置动态生成mock.json的内容 -
监听config文件夹 判断关于mock的配置项是否有改变重新生成mock.json
讯享网// scripts/webpack-init.js 在wenpack配置文件中初始化const path = require(‘path’)const fs = require(‘fs’)const {syncWalkDir} = require(’https://mp.weixin..com/util’)let confGlobal = {}let mockJsonData = {}exports.getConf = () => confGlobalexports.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.devconfGlobal = 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))</span></code><code><span class="code-snippet_outer"> console.log('build mocks: ->>>>>>>>>>>>>>>>>>>>>>>')</span></code><code><span class="code-snippet_outer"> mockFiles.forEach((filePath) => {</span></code><code><span class="code-snippet_outer"> const p = path.parse(filePath)</span></code><code><span class="code-snippet_outer"> const mockKey = p.name.substr(0, p.name.length - 5)</span></code><code><span class="code-snippet_outer"> console.log(mockKey, filePath)</span></code><code><span class="code-snippet_outer"> if (mockJson[mockKey]) {</span></code><code><span class="code-snippet_outer"> console.error(`有相同的mock文件名称\){p.name} 存在, filePath)</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> delete require.cache[require.resolve(filePath)]</span></code><code><span class="code-snippet_outer"> mockJson[mockKey] = require(filePath)</span></code><code><span class="code-snippet_outer"> })</span></code><code><span class="code-snippet_outer"> // 如果是打包环境, 最小化mock资源数据</span></code><code><span class="code-snippet_outer"> const mockMap = conf.mock || {}</span></code><code><span class="code-snippet_outer"> const buildMockJson = {}</span></code><code><span class="code-snippet_outer"> Object.keys(mockMap).forEach((key) => {</span></code><code><span class="code-snippet_outer"> const [name, method] = key.split('.')</span></code><code><span class="code-snippet_outer"> if (mockJson[name][method] && mockJson[name][method][mockMap[key]]) {</span></code><code><span class="code-snippet_outer"> if (!buildMockJson[name]) buildMockJson[name] = {}</span></code><code><span class="code-snippet_outer"> if (!buildMockJson[name][method]) buildMockJson[name][method] = {}</span></code><code><span class="code-snippet_outer"> buildMockJson[name][method][mockMap[key]] = mockJson[name][method][mockMap[key]]</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> })</span></code><code><span class="code-snippet_outer"> mockJsonData = buildMockJson</span></code><code><span class="code-snippet_outer"> fs.writeFileSync(path.join(__dirname, '/mock.json'), JSON.stringify(buildMockJson, null, '\t'))</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer"> // 监听配置文件目录下的config.js和config_default.js</span></code><code><span class="code-snippet_outer">const confPath = path.join(__dirname, '/config')</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer">if ((env = process.env.BUILD_ENV ? 'build' : 'dev') === 'dev') {</span></code><code><span class="code-snippet_outer"> fs.watch(confPath, async (event, filename) => {</span></code><code><span class="code-snippet_outer"> if (filename === 'config.js' || filename === 'config_default.js') {</span></code><code><span class="code-snippet_outer"> delete require.cache[path.join(confPath, filename)]</span></code><code><span class="code-snippet_outer"> delete require.cache[require.resolve('/config')]</span></code><code><span class="code-snippet_outer"> const config = require('/config')</span></code><code><span class="code-snippet_outer"> // console.log('config', JSON.stringify(config))</span></code><code><span class="code-snippet_outer"> const env = process.env.BUILD_ENV ? 'build' : 'dev'</span></code><code><span class="code-snippet_outer"> const confJson = env === 'build' ? config.conf.build : config.conf.dev</span></code><code><span class="code-snippet_outer"> if (JSON.stringify(confJson) !== JSON.stringify(confGlobal)) {</span></code><code><span class="code-snippet_outer"> this.init()</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> });</span></code><code><span class="code-snippet_outer">}</span></code></section></pre></section></pre><h4 style="text-align: left;margin: 15px 8px;"><strong><span style="color: rgb(26, 177, 204);">接口代理处理</span></strong></h4><h5 style="text-align: left;margin: 15px 8px;"><strong>onProxyReq和onProxyRes</strong></h5><section style="text-align: left;margin: 15px 8px;">实现上面思路里面说的onProxyReq和onProxyRes 响应处理</section><section style="text-align: left;margin: 15px 8px;">util.js</section><pre><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><section style="margin-left: 8px;margin-right: 8px;"><code><span class="code-snippet_outer">// scripts/api-proxy-cache </span></code><code><span class="code-snippet_outer">const fs = require('fs')</span></code><code><span class="code-snippet_outer">const path = require('path')</span></code><code><span class="code-snippet_outer">const moment = require('moment')</span></code><code><span class="code-snippet_outer">const {getConf, getMockJson} = require('https://mp.weixin..com/webpack-init')</span></code><code><span class="code-snippet_outer">const API_CACHE_DIR = path.join(__dirname, '/api-cache')</span></code><code><span class="code-snippet_outer">const {jsonParse, getBody} = require('https://mp.weixin..com/util')</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer">fs.mkdirSync(API_CACHE_DIR,{recursive: true})</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer">module.exports = {</span></code><code><span class="code-snippet_outer"> // 代理前处理</span></code><code><span class="code-snippet_outer"> onProxyReq: async (_, req, res) => {</span></code><code><span class="code-snippet_outer"> req.reqBody = await getBody(req)</span></code><code><span class="code-snippet_outer"> const {'mock-method': mockMethod, 'mock-key': mockKey} = req.headers</span></code><code><span class="code-snippet_outer"> // eslint-disable-next-line no-console</span></code><code><span class="code-snippet_outer"> console.log(Ajax 请求: \({mockKey}.\){mockMethod},req.method, req.url)</span></code><code><span class="code-snippet_outer"> // eslint-disable-next-line no-console</span></code><code><span class="code-snippet_outer"> req.reqBody && console.log(JSON.stringify(req.reqBody, null, '\t'))</span></code><code><span class="code-snippet_outer"> if (mockKey && mockMethod) {</span></code><code><span class="code-snippet_outer"> req.mockKey = mockKey</span></code><code><span class="code-snippet_outer"> req.mockMethod = mockMethod</span></code><code><span class="code-snippet_outer"> const conf = getConf()</span></code><code><span class="code-snippet_outer"> const mockJson = getMockJson()</span></code><code><span class="code-snippet_outer"> if (conf.mock && conf.mock[\({mockKey}.\){mockMethod}] && mockJson[mockKey] && mockJson[mockKey][mockMethod]) {</span></code><code><span class="code-snippet_outer"> // eslint-disable-next-line no-console</span></code><code><span class="code-snippet_outer"> console.log(use mock data \({mockKey}.\){mockMethod}:, conf.mock[\({mockKey}.\){mockMethod}], 'color: green')</span></code><code><span class="code-snippet_outer"> res.mock = true</span></code><code><span class="code-snippet_outer"> res.append('isMock','yes')</span></code><code><span class="code-snippet_outer"> res.send(mockJson[mockKey][mockMethod][conf.mock[\({mockKey}.\){mockMethod}]])</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> },</span></code><code><span class="code-snippet_outer"> // 响应缓存接口</span></code><code><span class="code-snippet_outer"> onProxyRes: async (res, req) => {</span></code><code><span class="code-snippet_outer"> const {method, url, query, path: reqPath, mockKey, mockMethod} = req</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer"> if (mockKey && mockMethod && res.statusCode === 200) {</span></code><code><span class="code-snippet_outer"><br /></span></code><code><span class="code-snippet_outer"> let resBody = await getBody(res)</span></code><code><span class="code-snippet_outer"> resBody = jsonParse(resBody)</span></code><code><span class="code-snippet_outer"> 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-consolefs.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配置
讯享网const config = require(‘.’)// config/webpack.config.jsconst {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,// 指明哪些路径映射到哪个htmlrewrites: config.rewrites,},host: ‘127.0.0.1’,hot: true,proxy: config.proxy,},}
-
一键部署 npm run deploy -
支持集群部署配置 -
是一个文件服务 -
是一个静态资源服务 -
在线可视化部署前端项目 -
配置热更新 -
在线Postman及接口文档 -
支持前端路由渲染, 支持模板 -
接口代理及路径替换 -
Web安全支持 Ajax请求验证,Referer 校验 -
支持插件开发和在线配置 可实现:前端模板参数注入、请求头注入、IP白名单、接口mock、会话、第三方登陆等等

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/147803.html