Vue.js 實戰——徹底解決跨域問題_18

一. 背景

在 Web 應用中,跨域是程序猿繞不過去的坎。

什麼是跨域:
當一個請求 url 的協議、域名、端口三者之間任意一個與當前頁面 url 不同即爲跨域。

舉例:在自己的應用 html 中,需要訪問微信或者新浪接口,這時瀏覽器就會提示跨域了。如下代碼所示:在業務代碼中訪問網絡地址來查詢天氣信息。

let url = 'http://t.weather.sojson.com/api/weather/city/101030100'
axios
  .get(url)
  .then(resp => {
    console.log('result:' + resp.data)
    this.content = resp.data
    alert('request success!')
  })
  .catch(e => {
    console.log('exception:' + e)
    alert('request failed!')
  })

運行結果如下:
avatar

爲什麼會跨域:
出於瀏覽器的同源策略限制。同源策略(SameOriginPolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說 Web 是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同源策略會阻止一個域的 javascript 腳本和另外一個域的內容進行交互。所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)和端口號(port)。

二. 目標

  1. 在 Vue 開發過程中(dev 模式),能夠正常跨域訪問;
  2. 在生產環境中,能夠正常跨域訪問,同時支持所有 vue 訪問的後臺 url 自定義;

三. 步驟

  1. 先確認 vue 版本,目前使用的是 2.5.11(參見 package.json);
  2. 先保證在通過 node.js(作爲 web 容器或者說 web 服務器),在本地調測時不跨域。通過在 webpack.config.js 中配置 proxy 代碼即可。
    在 webpack.config.js 中代碼如下:
let url = '/api/weather/city/101030100'
axios
  .get(url)
  .then(resp => {
    console.log('result:' + resp.data)
    this.content = resp.data
    alert('request success!')
  })
  .catch(e => {
    console.log('exception:' + e)
    alert('request failed!')
  })

在 webpack.config.js 中代碼如下:

  devServer: { //開發模式下使用的配置參數
    proxy: {
      '/api': {
        target: 'http://t.weather.sojson.com', // 接口域名
        changeOrigin: true, //是否跨域
        pathRewrite: {
          '^/api': '/api' //需要rewrite的,
        }
      }
    }
  }

運行結果如下:
avatar

  1. 爲了統一管理 url,保證前端可訪問任意位置後端和第三方(後端可能部署在本地,也有可能部署在遠端),則需要把所有訪問的 IP 和端口配置成全局共享,且不會被 webpack 打包壓縮。
    業務場景舉例如下:
    當用戶在本地調試時,使用的默認 IP+端口爲:127.0.0.1:8080(127.0.0.1 和 localhost 等價);當訪問的後端從本地改成內網其他地址(例如 IP 和端口爲:192.168.1.100:8080)上時,則需要修改所有後端訪問地址。
  2. 在 src 目錄下新增文件夾 config。在 config 目錄下新增 url.js。url.js 代碼如下:
const urlConfig = {
  baseUrl: '',
  apiUrl: ''
}
  1. 修改 webpack.config.js 中的配置。在 CopyWebpackPlugin 中新增 from/to 配置:
{
  from: __dirname + "/src/config", //源目錄
  to: "./config", //目標目錄
};
  1. 修改啓動 html。在 html 中注入 vue 代碼的 div 前面加入如下代碼。這樣可以保證在 vue 初始化過程中,url.js 已經加載。
<script type="text/javascript" src="config/url.js"></script>

注意:上述 src 中的路徑是以 webpack.config.js 中配置的相對路徑關係而定。

  1. 在 vue 的啓動 js 中,加入如下代碼:
Vue.prototype.url = urlConfig
  1. 在業務代碼中,添加對應的 url 前綴:
let url = this.url.apiUrl + '/api/weather/city/101030100'

這個第三方 url 爲隨意舉例,只是告訴大家可以這樣剝離所有的 url。實際上步驟 3 中的業務場景舉例纔是剝離 url 的主要原因。也就是說 baseUrl 抽出來的意義更大。baseUrl 的使用方法和上述 apiUrl 的使用方法相同。

  1. 解決在生產環境中的跨域問題。在生產環境中,devServer 參數並不起作用。所以要通過其他的方式來實現代理。最簡單的方式就是使用 nginx 作爲反向代理服務器,來統一代理直接訪問後端的 url 和訪問第三方 url。例如:訪問後臺的 url 爲 nginx 服務器的 80 端口,訪問第三方的 url 也爲 nginx 服務器 的 80 端口,這樣就不存在跨域問題了。而 nginx 則只需要解決根據不同的 url 做不同的跳轉即可。
  2. 安裝 nginx。macOS 執行命令如下。
brew install nginx
  1. 編輯 nginx.conf 文件(例如:我本機的位置爲:/usr/local/etc/nginx/nginx.conf,可考慮用 find 命令搜下),在 Server 內新增如下內容:
        listen       80;
        server_name  127.0.0.1;

        charset utf-8;

        location / {
            proxy_pass http://127.0.0.1:8080;
        }
        location /api {
            proxy_pass http://t.weather.sojson.com/api;
        }

保存完畢後,執行 nginx 啓動命令:

nginx -c /usr/local/etc/nginx/nginx.conf
  1. 保持訪問第三方 url 的代碼如下:
let url = '/api/weather/city/101030100'
axios
  .get(url)
  .then(resp => {
    console.log('result:' + resp.data)
    this.content = resp.data
    alert('request success!')
  })
  .catch(e => {
    console.log('exception:' + e)
    alert('request failed!')
  })
  1. 刪除 webpack.config.js 中的 proxy 配置。
  2. 通過地址欄訪問 nginx 的監聽地址:http://localhost(http默認端口是80,可以省略),效果如下:
    avatar
  3. 完整代碼路徑:github 鏈接

四. 總結

  1. 任何的業務問題,都不僅僅是單純的某一個知識點;
  2. 本文寫了 webpack 怎麼配置代理;怎麼把 url 獨立可配置,且不受打包壓縮影響;怎麼使用 nginx 反向代理解決跨域;
  3. 爲了儘量簡化業務代碼,有些涉及後端的業務代碼沒寫,閱讀起來可能有些費解;
  4. 這是第一次使用 markdown 來寫博客,有什麼好的建議請留言。

五. 參考

[1]什麼是跨域?跨域解決方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章