單頁應用history路由改造

需求

調用其它團隊流量站埋點統計時會截取錨點#,致使單頁應用分頁面流量分析獲取不到數據。而本項目迫切須要按菜單、特殊功能模塊統計流量狀況,等不及流量站的團隊開發升級版,因而選擇改造項目路由爲hostory模式。javascript

單頁應用路由實現思路

SPA,只加載一個HTML,頁面在用戶與應用程序交互時動態更新該頁。即理論上來講,只需加載一次頁面就能夠再也不請求(首屏耗時過長需按模塊chunk,預加載css,按需加載js),當點擊其餘子頁面時只會有相應的URL改變而不會從新加載。css

這種狀況下實現路由的過程分爲兩部分:html

  1. 更新URL頁面不刷新
  2. URL變化時執行頁面替換邏輯

如今主流有2種實現方式:前端

  1. history.pushState等觸發popstate事件
  2. location.hash的變化觸發hashchange事件

vue-router中提供了三種方式HTML5History(判斷是否支持)、HashHistory、AbstractHistory(用於Node環境,由於不涉及和瀏覽器地址相關記錄關聯在一塊兒;總體流程依舊和 HashHistory 是同樣的,只是這裏經過數組來模擬瀏覽器歷史記錄堆棧信息)vue

默認爲Hash模式,組件(components)映射到路由(routes),registerHook設置守衛入棧,在每次跳轉的時候,遞歸守衛集合,將觸發的守衛進行解析和執行。java

vue-router源碼閱讀webpack

實現過程 & 踩坑記錄

1. Router傳入配置項,設置mode爲'history'ios

export default new Router({ mode: 'history', routes })

2. 開發模式下,webpack 熱啓動配置 history 模式nginx

webpack屬性中historyApiFallback默認能將當前找不到的目錄重定向到主目錄默認index.html。
在 webpack 配置文件的devServer配置,將url重寫到本身配置的目錄:git

// webpack.dev.conf.js
devServer: {
...
  historyApiFallback: {
    rewrites: [
      { from: /.*/, to: '/index.html' }
    ],
  },
...
}

3. 部署預發後,刷新時資源404

開發過程一切正常,直到編譯打包發佈。

情況:從域名點擊菜單能正常跳轉,但刷新當前頁會報錯404。(Eg:從test.com點擊訪問/111正常,但直接訪問test.com/111報404)
頁面404

緣由:在History mode下,直接經過地址欄訪問url會被http server直接解析到該文件路徑,可是spa的路由是虛擬的,並不能直接找到這個file,因此會404。
解決思路:若是URL匹配不到任何靜態資源,就跳轉到默認的index.html,讓router去解析url, nginx中須要配置try_files。

# 當前項目使用了第一級'/'來區分產品,因此這裏匹配metric、stocktake
# 通常直接'location ~ /'便可
location ~ ^(/metric|/stocktake) {
  try_files $uri $uri/ /index.html;
}

具體配置官方提供了:其它服務器配置

4. 頁面再也不404,開始報語法錯誤

報錯以下
控制檯報錯
DOM狀況:
DOM狀況
資源狀況:
資源狀況

緣由:修改成history模式後,相對定位再也不適用,資源會根據當前刷新路徑變動訪問路徑(注意上圖資源狀況訪問的文件夾)。在此項目中靜態資源vendor.dll.的引入路徑有問題,需將相對路徑'./'修改成絕對路徑'/'
index.html引入路徑

5. 頁面正常加載,但後端接口被統一重定向

緣由:step3時在nginx處配置了try_files 加上後端接口也是/metric、/stocktake開頭。因而就不幸地被nginx匹配到規則統一做跳轉處理了。
解決:區分接口與頁面。在項目中統一對後端接口加'/api/',nginx新建規則匹配'/api/'並做相應跳轉。

// 在發請求的公共方法處添加'/api/'
function ajax(url, type, params, opt = '') {
  return Q.Promise((resolve, reject) => {
    const config = {
      method: type === 'get' ? 'get' : 'post',
      // TODO: '/api/'暫時做爲nginx區分重定向的匹配字段 待與兆華協商統一 不與路由命名重複
      url: C.HOST + '/api/' + url,
      params: type === 'get' ? params : null,
      data: configData(type, params, opt)
    }
    axios(config).then(checkStatus).then(checkCode).then((response) => {
      resolve(response)
    }).catch((err) => {
      console.log(err)
      if (err.msg) Notice(err.msg)
      reject(err)
    })
  })
}

nginx中添加以下匹配規則並重寫url

```
# 後臺三類開頭對應請求不一樣服務器
location ~ ^/api/system/ {
  rewrite ^/api/(.*) /$1 break;
  proxy_pass http://assets-api-yf.jd.com;
  proxy_redirect          off;
  proxy_set_header        X-Real-IP       $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
}

location ~ ^/api/metric/ {
    rewrite ^/api/(.*) /$1 break;
    proxy_pass http://metrics-api-yf2.jd.com;
  proxy_redirect          off;
  proxy_set_header        X-Real-IP       $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
}

location ~ ^/api/stocktake/ {
  rewrite ^/api/(.*) /$1 break;
  proxy_pass http://meta-api-yf.jd.com;
  proxy_redirect          off;
  proxy_set_header        X-Real-IP       $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
}

```

6. 切換產品菜單時會刷新

由於使用的是window.location,替換成this.$router.push便可

7. 非法輸入路由時,未正常重定向到首頁,直接404

情況:地址欄輸入test.com/ffsdfdsge,顯示'404 Not Found',並未重定向到首頁。
解決:nginx配置error_page。

server {
   ...
   error_page 404 /index.html;
   ...
}

前端小菜鳥一枚,如表述有誤,懇請各位大神指正~

相關文章
相關標籤/搜索