假设开发的时候存在以下几套环境:
-
自己本地的
MOCK
环境(自己造一些随心所欲的数据) -
RAP
平台环境(与后端约定好数据格式) -
后端开发人员的本地环境(与后端联调)
-
阿里云1测试环境(部署到测试环境,测试人员测试)
-
阿里云2测试环境(环境紧张,需要部署到别的测试环境进行测试)
-
阿里云3测试环境(公司不差钱,买了至少5个服务器)
-
阿里云4测试环境
-
阿里云5测试环境
......
是的,就是有这么多的环境等着你去访问它们。那么跑前端代码的时候如何才能与各个环境进行联调呢?最简单直接的办法就是修改proxy
选项中的target
,然后再重新执行开发脚本。这个方法在项目小的情况你还能忍受,如果项目大的话,过长的编译时间将直接消磨掉你的工作激情。
所以本文提出了一个能让你在联调时无缝切换不同环境的方案。如果你没用到代理或者后端设置了跨域资源共享,这篇文章看看也无妨。如果你用到了http-proxy-middleware来处理跨域的问题,那这篇文章你更得看看。
第一步、配置环境
定义一个变量来承载所需要的路由键值对:
测试环境
let PROXY_ROUTER = {
'dev-aliyun1.test.com': 'https://m-aliyun1.test.com:10454',
'dev-aliyun2.test.com': 'https://m-aliyun2.test.com:10454',
'dev-aliyun3.test.com': 'https://m-aliyun3.test.com:10454',
'dev-aliyun4.test.com': 'https://m-aliyun4.test.com:10454',
'dev-aliyun5.test.com': 'https://m-aliyun5.test.com:10454'
}
复制代码
每个开发域名dev-aliyunX.test.com
域名都对应一个测试环境。
RAP环境
let PROXY_ROUTER = {
...
'dev-rap.test.com': 'http://192.168.4.102:9999/mockjsdata/400',
}
复制代码
用过RAP
的同学都知道,不同模块挂着不同的id
(就是?url中的400),那这样是不是得把所需要的id
都写上去?其实RAP
提供了项目路由功能,能把不同模块的id
路由到一个模块id
上:
各个模块之间使用逗号隔开,这样只需要写一个主id就行。
后端开发本地环境
// 填写后端开发的ip地址和端口号
const devHostName = '172.16.9.xx'
const devServerPort = '8085'
let PROXY_ROUTER = {
...
'dev-debug.test.com': `http://${devHostName}:${devServerPort}`
}
复制代码
与后端开发联调就避免不了要重启项目了,因为后端开发的ip未知,需要手动填写。
本地mock环境
src目录下新建mock.js
文件,写入:
import Mock from 'mockjs' // 引入mock
import { mockMemberInfo } from './memberinfo'
Mock.mock('/api/rights/memberInfo', 'post', {
{
'code': '100',
'result': {
'level': 'trial',
'name': '沈浩',
},
'resultDes': '',
'success': true
}
})
复制代码
在main.js
中写入:
if (process.env.NODE_ENV !== 'production' && window.location.hostname.indexOf('test') < 0) {
require('./mock.js')
}
复制代码
只有在开发环境并且是代理到本地mock的情况下才启用。无需在路由表中写入对应的环境。
注:所有的开发域名都需要配置host,如下:127.0.0.1 dev-m-aliyun1.test.com ...
第二步、配置路由
统一前缀
统一对所有的接口进行代理处理,如果用了axios
请求库,可以这么配置:
axios.defaults.baseURL = process.env.NODE_ENV === 'production' ? '' : '/api'
复制代码
路由分发
http-proxy-middleware
提供了一个router
选项(接收一个对象或者函数):可以使用host
或者path
或者host+path
匹配特定的请求来重写option.target
,也就是说router
命中的url
优先级高于target
配置的url
。
处理前缀为/api
的接口:
let proxyTable = {
'/api': ''
}
Object.entries(proxyTable).forEach(([key, value], index) => {
proxyObj[key] = {
target: value || 'http://localhost:8080',
changeOrigin: true,
pathRewrite (path, req) {
return path.replace(/\/api/, '')
},
router (req) {
let hostname = req.headers.host.split(':')[0]
return value === '' ? PROXY_ROUTER[hostname] : value
}
}
})
复制代码
通过接口请求的host
,来匹配PROXY_ROUTER
中对应的环境。如果匹配上了,router function
返回匹配上的环境地址从而覆盖掉target
。如果没匹配上,router function
返回undefined
,target
生效,即代理本地mock环境。
在现实开发中还可能会出现一种奇葩的需求:你在a环境联调,但有的接口还在b环境上。要解决这种问题,你只需要在proxyTable
中加上:
let proxyTable = {
'/api/cms/renderData': 'https://m-aliyun2.test.com:10454',
'/api': ''
}
复制代码
这样无论你怎么切换域名,/api/cms/renderData
始终将会被代理到https://m-aliyun2.test.com:10454
环境上。
顺序很重要,第一个匹配上的将会生效。
总结
以vue-cli3
为脚手架,在根目录新建一个proxy.config.js
文件:
let proxyObj = {}
// 与开发联调的时候由于未知 需手动填写
const devHostName = '172.16.9.xx'
const devServerPort = '8085'
// 代理路由表
let PROXY_ROUTER = {
'dev-m-aliyun1.test.com': 'https://m-aliyun1.test.com:10454', // 代理到阿里云1测试环境
'dev-m-aliyun2.test.com': 'https://m-aliyun2.test.com:10454', // 代理到阿里云2测试环境
'dev-m-aliyun3.test.com': 'https://m-aliyun3.test.com:10454', // 代理到阿里云3测试环境
'dev-m-aliyun4.test.com': 'https://m-aliyun4.test.com:10454', // 代理到阿里云4测试环境
'dev-m-aliyun5.test.com': 'https://m-aliyun5.test.com:10454', // 代理到阿里云5测试环境
'dev-rap.test.com': 'http://192.168.4.102:9999/mockjsdata/400', // 代理到rap环境
'dev-debug.test.com': `http://${devHostName}:${devServerPort}` // 代理到后端开发人员的ip环境
}
// 代理接口
let proxyTable = {
'/api/cms/renderData': 'https://m-aliyun2.test.com:10454',
'/api': ''
}
Object.entries(proxyTable).forEach(([key, value], index) => {
proxyObj[key] = {
target: value || 'http://localhost:8080', // 代理到本地mock环境
changeOrigin: true,
pathRewrite (path, req) {
return path.replace(/\/api/, '')
},
router (req) {
let hostname = req.headers.host.split(':')[0]
return value === '' ? PROXY_ROUTER[hostname] : value
}
}
})
module.exports = proxyObj
复制代码
在vue.config.js
文件中修改:
const proxyBase = require('./proxy.config')
module.exports = {
devServer: {
proxy: proxyBase,
disableHostCheck: true // 新版的webpack-dev-server出于安全考虑,默认检查hostname,如果hostname不是配置内的,将中断访问
},
...
}
复制代码
上述代理配置以后,只需要通过切换域名就可以无缝切换相应的测试环境,无需重启前端服务,这对所处多个开发环境的前端开发者来说大大提高了工作效率。
其实,代理只是个辅助我们开发的工具,将代理硬编码到项目代码中去的话,可维护性的确会差一点。可是,对于我们软件开发来说,工具只能成为我们的充分项,而不能成为我们的必要项。所以,我的这个方案各有利弊,大家可以依照自己项目中的实际需求来酌情使用。