前端路由以及vue路由的hash模式與history模式

什麼是路由javascript

路由這個概念最早是後端出現的。在之前用模板引擎開發頁面時,常常會看到這樣php

http://hometown.xxx.edu.cn/bbs/forum.php
複製代碼

有時還會有帶.asp或.html的路徑,這就是所謂的SSR(Server Side Render),經過服務端渲染,直接返回頁面。html

其響應過程是這樣的前端

  1. 瀏覽器發出請求
  2. 服務器監聽到80端口(或443)有請求過來,並解析url路徑
  3. 根據服務器的路由配置,返回相應信息(能夠是 html 字串,也能夠是 json 數據,圖片等)
  4. 瀏覽器根據數據包的Content-Type來決定如何解析數據

簡單來講路由就是用來跟後端服務器進行交互的一種方式,經過不一樣的路徑,來請求不一樣的資源,請求不一樣的頁面是路由的其中一種功能。vue

前端路由的誕生的原因java

前端路由的出現要從 ajax 開始,爲何?且聽下面分析react

Ajax,全稱 Asynchronous JavaScript And XML,是瀏覽器用來實現異步加載的一種技術方案。在 90s 年代初,大多數的網頁都是經過直接返回 HTML 的,用戶的每次更新操做都須要從新刷新頁面。及其影響交互體驗,隨着網絡的發展,迫切須要一種方案來改善這種狀況。nginx

1996,微軟首先提出 iframe 標籤,iframe 帶來了異步加載和請求元素的概念,隨後在 1998 年,微軟的 Outloook Web App 團隊提出 Ajax 的基本概念(XMLHttpRequest的前身),並在 IE5 經過 ActiveX 來實現了這項技術。在微軟實現這個概念後,其餘瀏覽器好比 Mozilia,Safari,Opera 相繼以 XMLHttpRequest 來實現 Ajax。不過在 IE7 發佈時,微軟選擇了妥協,兼容了 XMLHttpRequest 的實現。ajax

有了 Ajax 後,用戶交互就不用每次都刷新頁面,體驗帶來了極大的提高。vue-router

但真正讓這項技術發揚光大的,(。・∀・)ノ゙仍是後來的 Google Map,它的出現向人們展示了 Ajax 的真正魅力,釋放了衆多開發人員的想象力,讓其不只僅侷限於簡單的數據和頁面交互,爲後來異步交互體驗方式的繁榮發展帶來了根基。

單頁應用。單頁應用不只僅是在頁面交互是無刷新的,連頁面跳轉都是無刷新的,爲了實現單頁應用,因此就有了前端路由。

單頁應用的概念是伴隨着 MVVM 出現的。最先由微軟提出,而後他們在瀏覽器端用 Knockoutjs 實現。但這項技術的強大之處並未當時的開發者體會到,多是由於 Knockoutjs 實現過於複雜,致使沒有大面積的擴散。

一樣,此次接力的選手依然是 Google。Google 經過 Angularjs 將 MVVM 及單頁應用發揚光大,讓前端開發者可以開發出更加大型的應用,職能變得更大了。(不得不感慨,微軟 跟 Google 都是偉大的公司)。隨後都是你們都知道的故事,前端圈開始獲得了爆發式的發展,陸續出現了不少優秀的框架。

從 vue-router 來看前端路由實現原理

前端路由的實現其實很簡單。

本質上就是檢測 url 的變化,截獲 url 地址,而後解析來匹配路由規則。

可是這樣有人就會問:url 每次變化都會刷新頁面啊?頁面都刷新了,JavaScript 怎麼檢測和截獲 url?

在 2014 年以前,你們是經過 hash 來實現路由,url hash 就是相似於

https://segmentfault.com/a/1190000011956628#articleHeader2
複製代碼

這種 #。後面 hash 值的變化,並不會致使瀏覽器向服務器發出請求,瀏覽器不發出請求,也就不會刷新頁面。另外每次 hash 值的變化,還會觸發 hashchange 這個事件,經過這個事件咱們就能夠知道 hash 值發生了哪些變化。

讓咱們來整理思路,假如咱們要用 hash 的模式實現一個路由,那麼流程應該是這樣的。

在這裏插入圖片描述
vue-router hash

/** * 添加 url hash 變化的監聽器 */
setupListeners () {
  const router = this.router

  /** * 每當 hash 變化時就解析路徑 * 匹配路由 */
  window.addEventListener('hashchange', () => {
    const current = this.current
    /** * transitionTo: * 匹配路由 * 並經過路由配置,把新的頁面 render 到 ui-view 的節點 */
    this.transitionTo(getHash(), route => {
      replaceHash(route.fullPath)
    })
  })
}
複製代碼

檢測到 hash 的變化後,就能夠經過替換 DOM 的方式來實現頁面的更換。

14年後,由於HTML5標準發佈。多了兩個 API,pushState 和 replaceState,

經過這兩個 API 能夠改變 url 地址且不會發送請求。同時還有 onpopstate 事件。經過這些就能用另外一種方式來實現前端路由了,但原理都是跟 hash 實現相同的。用了 HTML5 的實現,單頁路由的 url 就不會多出一個#,變得更加美觀。但由於沒有 # 號,因此當用戶刷新頁面之類的操做時,瀏覽器仍是會給服務器發送請求。爲了不出現這種狀況,因此這個實現須要服務器的支持,須要把全部路由都重定向到根頁面。

在這裏插入圖片描述
vue-router history

export class HTML5History extends History {
  constructor (router, base) {
    super(router, base)
    /** * 原理仍是跟 hash 實現同樣 * 經過監聽 popstate 事件 * 匹配路由,而後更新頁面 DOM */
    window.addEventListener('popstate', e => {
      const current = this.current

      // Avoiding first `popstate` event dispatched in some browsers but first
      // history route not updated since async guard at the same time.
      const location = getLocation(this.base)
      if (this.current === START && location === initLocation) {
        return
      }

      this.transitionTo(location, route => {
        if (supportsScroll) {
          handleScroll(router, route, current, true)
        }
      })
    })
  }

  go (n) {
    window.history.go(n)
  }

  push (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // 使用 pushState 更新 url,不會致使瀏覽器發送請求,從而不會刷新頁面
      pushState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }

  replace (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // replaceState 跟 pushState 的區別在於,不會記錄到歷史棧
      replaceState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }
}
複製代碼

對於vue這類漸進式前端開發框架,爲了構建 SPA(單頁面應用),須要引入前端路由系統,這也就是 Vue-Router 存在的意義。前端路由的核心,就在於 —— 改變視圖的同時不會向後端發出請求。

區別

  • hash --- 即地址欄 URL 中的 # 符號(此 hash 不是密碼學裏的散列運算)。好比這個 URL:www.abc.com/#/hello,hash 的值爲 #/hello。
    • 它的特色在於:hash 雖然出如今 URL 中,但不會被包括在 HTTP 請求中,對後端徹底沒有影響,所以改變 hash 不會從新加載頁面。
  • history --- 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(須要特定瀏覽器支持)這兩個方法應用於瀏覽器的歷史記錄棧,在當前已有的 back、forward、go 的基礎之上,它們提供了對歷史記錄進行修改的功能。只是當它們執行修改時,雖然改變了當前的 URL,但瀏覽器不會當即向後端發送請求

所以能夠說,hash 模式和 history 模式都屬於瀏覽器自身的特性,Vue-Router 只是利用了這兩個特性(經過調用瀏覽器提供的接口)來實現前端路由. 使用場景 通常場景下,hash 和 history 均可以,除非你更在乎顏值,# 符號夾雜在 URL 裏看起來確實有些不太美麗。

若是不想要很醜的 hash,咱們能夠用路由的 history 模式,這種模式充分利用 history.pushState API 來完成URL 跳轉而無須從新加載頁面。

另外,根據 Mozilla Develop Network 的介紹,調用 history.pushState() 相比於直接修改 hash,存在如下優點:

  • pushState() 設置的新 URL 能夠是與當前 URL 同源的任意 URL;而 hash 只可修改 # 後面的部分,所以只能設置與當前 URL 同文檔的 URL;
  • pushState() 設置的新 URL 能夠與當前 URL 如出一轍,這樣也會把記錄添加到棧中;而 hash 設置的新值必須與原來不同纔會觸發動做將記錄添加到棧中;
  • pushState() 經過 stateObject 參數能夠添加任意類型的數據到記錄中;而 hash 只可添加短字符串;
  • pushState() 可額外設置 title 屬性供後續使用。

固然啦,history 也不是樣樣都好。SPA 雖然在瀏覽器裏遊刃有餘,但真要經過 URL 向後端發起 HTTP 請求時,二者的差別就來了。尤爲在用戶手動輸入 URL 後回車,或者刷新(重啓)瀏覽器的時候,會報一個404的錯誤,須要後端配置重定向到正確的路由便可解決該問題。

總結

  1. hash 模式下,僅 hash 符號以前的內容會被包含在請求中,如 www.abc.com,所以對於後端來講,即便沒有作到對路由的全覆蓋,也不會返回 404 錯誤。
  2. history 模式下,前端的 URL 必須和實際向後端發起請求的 URL 一致,如 www.abc.com/book/id。若是後段沒有對 /book/id 的路由作處理,將返回 404 錯誤。Vue-Router 官網裏如此描述:「不過這種模式要玩好,還須要後臺配置支持……因此呢,你要在服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。」
  3. 結合自身例子,對於通常的 Vue + Vue-Router + Webpack + XXX 形式的 Web 開發場景,用 history 模式便可,只需在後端(Apache 或 Nginx)進行簡單的路由配置,同時搭配前端路由的 404 頁面支持。

模板回覆

大牛回答:hash模式url裏面永遠帶着#號,咱們在開發當中默認使用這個模式。那麼何時要用history模式呢?若是用戶考慮url的規範那麼就須要使用history模式,由於history模式沒有#號,是個正常的url適合推廣宣傳。固然其功能也有區別,好比咱們在開發app的時候有分享頁面,那麼這個分享出去的頁面就是用vue或是react作的,我們把這個頁面分享到第三方的app裏,有的app裏面url是不容許帶有#號的,因此要將#號去除那麼就要使用history模式,可是使用history模式還有一個問題就是,在訪問二級頁面的時候,作刷新操做,會出現404錯誤,那麼就須要和後端人員配合讓他配置一下apache或是nginx的url重定向,重定向到你的首頁路由上就ok啦。

相關文章
相關標籤/搜索