Vue 項目代理設置的優化

Vue 項目代理設置的優化

Vue 類的項目開發中項目結構基本都是相似於 Vue-cli 生成的方式,
這種方式開發中,最經常使用到的模式是開啓代理進行 mock 調試或遠程調試,
也就是使用了 Vue-cli 設置的配置 proxyTable 或者直接使用 Webpack-dev-server
提供的 proxy 選項。它是採用了 http-proxy 庫,因此具體配置可查看:
https://github.com/nodejitsu/node-http-proxy#optionsjavascript

利用配置的這些參數咱們能夠作更爲靈活的配置,達到更好的效果vue

使用需求

假設咱們本地開發目前如下幾種狀態:java

  1. 本地開發,數據使用本地的 mock Server
  2. 涉及權限接口使用本地 mock 數據,其餘所有使用指定的一臺遠程機器
  3. 涉及權限接口使用本地 mock 數據,其餘數據分接口使用不一樣的遠程機器
  4. 全部接口使用同一臺遠程機器

方案

先看下經典的proxyTable 寫法:node

proxyTable: {
  '/authui/': {
    target: target,
    changeOrigin: true
  },
  '/vendor/': {
    target: target,
    changeOrigin: true
  }
}

其中用到了 changeOrigin 字段,主要是用於改變請求的 header。細化下需求:webpack

  1. 本地開發:target 指向 localhost 的某個端口便可。至於 host 的驗證確定是不須要的
  2. 部分本地,其餘固定的一臺遠程機器:須要配置 localhost 和遠程的地址,遠程地址多半是須要驗證 host 的
  3. 同二,但機器有多臺:須要手動配置多臺機器
  4. 同一臺遠程機器,此時機器可能要嚴格驗證,即 IP 也必須使用域名,配置好系統 host 纔可以使用

說明:嚴格驗證 host 和普通驗證 host 區別主要在於嚴格驗證時,請求的 url 必須是遠程機器的域名,
不能直接修改請求的 header 的 host 實現,即必須在系統 host 層面配置好域名。git

分析完成具體需求好,就開始準備實現的方式。原有開發方式是執行 npm run dev,若是咱們須要在命令行層面添加配置,
就須要設置爲 npm run dev --param=paramvalue 的方式。對於使用 npm 的 script 腳本執行的命令,
它參數的獲取沒法經過 process.env 得到,並且經過 process.env.npm_config_paramName 的方式獲取,
使用現成的命令行參數解析庫也不是很方便,但爲了省事,暫時仍是使用 npm 自帶的解析。github

請求發起過程當中須要如下幾個參數:web

  • host: 發起請求須要指向的 host,可能每臺機器驗證並不相同
  • port: 代理轉發的端口
  • receiver: 用於 push 的遠程地址,內包含了 ip 地址,爲了省事,沒有單獨列出 ip 地址

而後定義代理請求自定義類型,用於配置:npm

  • local: 本地地址,即 localhost
  • remote: 指定的遠程機器
  • 其餘自定義類型:用於在配置文件中已經指定的其餘類型
  • 原版本的請求,如 'http://xxx' 或者 Object 類型的配置,此類代理永不處理

根據須要,咱們添加如下幾個參數用於控制代理指向地址:api

  1. rd: 遠程機器的地址
  2. focus: 嚴格模式,全部自定義類型的代理轉換爲指定的 rd 機器,只在存在 rd 參數時可用
  3. allLocal:自定義類型代理所有指向本地
  4. 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 這種形式,能夠有相似的庫吧,後續能夠作爲深刻的內容

相關文章
相關標籤/搜索