vue 类的项目开发中项目结构基本都是类似于 Vue-cli 生成的方式,
这种方式开发中, 最常用到的模式是开启代理进行 mock 调试或远程调试,
也就是使用了 Vue-cli 设置的配置 proxyTable 或者直接使用 webpack-dev-server
提供的 proxy 选项. 它是采用了 http-proxy 库, 所以具体配置可查看:
https://github.com/nodejitsu/node-http-proxy#options
利用配置的这些参数我们可以做更为灵活的配置, 达到更好的效果
使用需求
假设我们本地开发目前以下几种状态:
本地开发, 数据使用本地的 mock Server
涉及权限接口使用本地 mock 数据, 其他全部使用指定的一台远程机器
涉及权限接口使用本地 mock 数据, 其他数据分接口使用不同的远程机器
所有接口使用同一台远程机器
方案
先看下经典的 proxyTable 写法:
- proxyTable: {
- '/authui/': {
- target: target,
- changeOrigin: true
- },
- '/vendor/': {
- target: target,
- changeOrigin: true
- }
- }
其中用到了 changeOrigin 字段, 主要是用于改变请求的 header. 细化下需求:
本地开发: target 指向 localhost 的某个端口即可. 至于 host 的验证肯定是不需要的
部分本地, 其他固定的一台远程机器: 需要配置 localhost 和远程的地址, 远程地址多半是需要验证 host 的
同二, 但机器有多台: 需要手动配置多台机器
同一台远程机器, 此时机器可能要严格验证, 即 IP 也必须使用域名, 配置好系统 host 才可使用
说明: 严格验证 host 和普通验证 host 区别主要在于严格验证时, 请求的 url 必须是远程机器的域名,
不能直接修改请求的 header 的 host 实现, 即必须在系统 host 层面配置好域名.
分析完成具体需求好, 就开始准备实现的方式. 原有开发方式是执行 npm run dev, 如果我们需要在命令行层面添加配置,
就需要设置为
npm run dev --param=paramvalue
的方式. 对于使用 npm 的 script 脚本执行的命令,
它参数的获取无法通过 process.env 获得, 而且通过
process.env.npm_config_paramName
的方式获取,
使用现成的命令行参数解析库也不是很方便, 但为了省事, 暂时还是使用 npm 自带的解析.
请求发起过程中需要以下几个参数:
host: 发起请求需要指向的 host, 可能每台机器验证并不相同
port: 代理转发的端口
receiver: 用于 push 的远程地址, 内包含了 ip 地址, 为了省事, 没有单独列出 ip 地址
然后定义代理请求自定义类型, 用于配置:
local: 本地地址, 即 localhost
remote: 指定的远程机器
其他自定义类型: 用于在配置文件中已经指定的其他类型
原版本的请求, 如'http://xxx/' 或者 Object 类型的配置, 此类代理永不处理
根据需要, 我们添加以下几个参数用于控制代理指向地址:
rd: 远程机器的地址
focus: 严格模式, 所有自定义类型的代理转换为指定的 rd 机器, 只在存在 rd 参数时可用
allLocal: 自定义类型代理全部指向本地
host: 请求发现是否使用 host, 而不是 IP 地址
总结一下 (序号指向前面的需求):
需要使用 host 进行访问的情形: 4
需要更改 host: 除 localhost 外都需要更改
需要对已有类型进行转换: 1: 需要将所有自定义类型都转换为 local, 2 和 3: 什么也不转换, 4: 所有的自定义类型全部转换为 remote 类型
这么一看, 貌似 host 是不需要的, 它的存在主要是针对某些 机器可能需要使用 host 的方式, 所以还是保留一下.
实现
逻辑理清了就很简单了, 配置文件设置为:
- module.export = {
- rd1: {
- host: 'dev1.example.com',
- port: 8838,
- receiver: 'http://1.1.1.1:8888/receiver'
- },
- rd2: {
- host: 'dev2.example.com',
- port: 8838,
- receiver: 'http://1.1.1.1:8888/receiver'
- }
- }
proxyTable 配置方式
- {
- proxyTable: {
- '/api1': 'remote',
- '/api2': 'rd2',
- '/auth/xx': 'local',
- '/other': 'http://example.com'
- }
- }
获取 proxyTable 的代码:
- // 处理 proxyTable
- const releaseConfig = require('../config/release.conf.js')
- const rdConfig = releaseConfig[process.env.npm_config_rd]
- const isAllRemote = process.env.npm_config_focus
- const useHost = isAllRemote || process.env.npm_config_host
- // 是否本机开发, 本机开发 remote 会指向 local
- const isAllLocal = process.env.npm_config_allLocal
- module.exports = function (proxy) {
- const localUrl = `http://localhost:${proxy.localProxyPort}`
- const defaultHost = proxy.defaultRdHost || 'dev-example.com'
- const localProxyPort = proxy.localProxyPort || 8787
- const finalConfig = formatReleaseConfig(releaseConfig)
- const remote = finalConfig.remote || {}
- if (process.env.npm_config_rd) {
- if (!rdConfig) {
- throw new TypeError('RD 机器名称不存在, 请在 config/release.conf.js 中进行配置')
- }
- if (!remote.ip) {
- throw new Error('请配置 rd 机器的 receiver')
- }
- }
- if (isAllRemote && !rdConfig) {
- throw new TypeError('focus 只能在提供了 rd 名称后可设置')
- }
- function formatReleaseConfig (config) {
- const result = {}
- Object.keys(config).map((key) => {
- const value = config[key]
- const ipMatch = (value.receiver || '').match(/:\/\/(.*?):\d/)
- const ip = ipMatch && ipMatch[1]
- result[key] = {
- ip,
- host: value.host || defaultHost,
- port: value.port || '8391'
- }
- })
- // 设置 remote
- if (rdConfig) {
- const ipMatch = (rdConfig.receiver || '').match(/:\/\/(.*?):\d/)
- const ip = ipMatch && ipMatch[1]
- result.remote = {
- ip,
- host: rdConfig.host || defaultHost,
- port: rdConfig.port || '8391'
- }
- }
- // 设置 local
- result.local = {
- ip: 'localhost',
- host: 'localhost',
- port: localProxyPort
- }
- return result
- }
- function setProxy (proxyTable) {
- const result = {}
- Object.keys(proxyTable).forEach((api) => {
- let type = proxyTable[api]
- const isCustomType = typeof type === 'string' && !/^http/.test(type)
- if (isCustomType && type !== 'remote' && type !== 'local' && !finalConfig[type]) {
- throw new TypeError(` 代理类型 ${type} 不正确, 请提供 http 或 https 类型的接口, 或者指定正确的 release 机器名称 `)
- }
- if (type === 'remote' && !finalConfig.remote) {
- type = 'local'
- }
- if (isCustomType) {
- if (isAllRemote && type !== 'remote') {
- type = 'remote'
- }
- if (isAllLocal && type !== 'local') {
- type = 'local'
- }
- }
- const targetConfig = finalConfig[type]
- let target = type
- if (targetConfig) {
- target = {
- target: `http://${useHost ? targetConfig.host : targetConfig.ip}:${targetConfig.port}`,
- // 使用 host 时需要转换, 其他不需要转换
- headers: {
- host: `${targetConfig.host}:${targetConfig.port}`
- }
- }
- }
- result[api] = target
- })
- return result
- }
- return {
- proxyTable: setProxy(proxy.proxyTable),
- host: remote.host || defaultHost
- }
- }
用法
用法中需要配置两种指向: 系统 host 和浏览器代理 Host.
之所以要两种 host, 本质上是因为接口使用的域名
和我们的本地访问的域名是相同的, 同一域名无法指向两个地址, 所以相当于对浏览器端进行了拦截.
系统 host 推荐使用 switchHost 进行切换, 浏览器推荐使用 whistle 进行切换.
本地开发
host 配置: 无
whistle 配置: 默认的域名
127.0.0.1 dev.example.com
启动命令:
- npm run dev
- npm run dev --allLocal
注: 此时 proxyTable 中配置的 remote 全部转换为 local, 在 allLocal 参数时将所有自定义类型转换为 local
本地 + 1 台远程
host 配置: 无
whistle 配置: 默认的域名
- 127.0.0.1 dev1.example.com
- 127.0.0.1 dev2.example.com
启动命令:
- npm run dev --rd=rd1
- npm run dev --rd=rd1 --host
注: --host 表示使用访问使用 host 而非 ip, 使用时需要 host 地址
本地 + n 台远程
host 配置: 无
whistle 配置: 默认的域名
- 127.0.0.1 dev1.example.com
- 127.0.0.1 dev2.example.com
proxyTable 配置:
- {
- proxyTable: {
- '/api1': 'rd1',
- '/api2': 'rd2',
- '/auth/xx': 'local',
- '/other': 'http://example.com'
- }
- }
启动命令:
npm run dev
远程 1 台机器
host 配置:
- 1.1.1.1 dev1.example.com
- 1.1.1.1 dev2.example.com
whistle 配置: 默认的域名
- 127.0.0.1 dev1.example.com
- 127.0.0.1 dev2.example.com
启动命令:
npm run dev --rd=rd1 --focus
总结
细挖需求, 可能还有更简单的方式, 在大部分情况下能够减少代码修改,
是 webpack 配置型的实现吧. 当然, 方式并不完美, 尤其在 mac 下,
居然不能支持 --rd xx 这种形式, 可以有类似的库吧, 后续可以做为深入的内容
来源: https://www.cnblogs.com/dreamless/p/8856900.html