成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

前端路由跳轉(zhuǎn)基本原理

SillyMonkey / 1313人閱讀

摘要:目前前端三杰都推介單頁面應(yīng)用開發(fā)模式,在路由切換時(shí)替換中最小修改的部分,來減少原先因?yàn)槎囗搼?yīng)用的頁面跳轉(zhuǎn)帶來的巨量性能損耗。

目前前端三杰 Angular、React、Vue 都推介單頁面應(yīng)用 SPA 開發(fā)模式,在路由切換時(shí)替換 DOM Tree 中最小修改的部分 DOM,來減少原先因?yàn)槎囗搼?yīng)用的頁面跳轉(zhuǎn)帶來的巨量性能損耗。它們都有自己的典型路由解決方案,@angular/router、react-router、vue-router。

一般來說,這些路由插件總是提供兩種不同方式的路由方式: Hash 和 History,有時(shí)也會(huì)提供非瀏覽器環(huán)境下的路由方式 Abstract,在 vue-router 中是使用了外觀模式將幾種不同的路由方式提供了一個(gè)一致的高層接口,讓我們可以更解耦的在不同路由方式中切換。

值得一提的是,Hash 和 History 除了外觀上的不同之外,還一個(gè)區(qū)別是:Hash 方式的狀態(tài)保存需要另行傳遞,而 HTML5 History 原生提供了自定義狀態(tài)傳遞的能力,我們可以直接利用其來傳遞信息。

下面我們具體看看這兩種方式都有哪些特點(diǎn),并提供簡單的實(shí)現(xiàn),比如基本的功能,更復(fù)雜的功能比如懶加載、動(dòng)態(tài)路徑匹配、嵌套路由、路由別名等等,可以關(guān)注一下后面的 vue-router 源碼解讀方面的博客。

1. Hash 1.1 相關(guān) Api

Hash 方法是在路由中帶有一個(gè) #,主要原理是通過監(jiān)聽 # 后的 URL 路徑標(biāo)識(shí)符的更改而觸發(fā)的瀏覽器 hashchange 事件,然后通過獲取 location.hash 得到當(dāng)前的路徑標(biāo)識(shí)符,再進(jìn)行一些路由跳轉(zhuǎn)的操作,參見 MDN

location.href:返回完整的 URL

location.hash:返回 URL 的錨部分

location.pathname:返回 URL 路徑名

hashchange 事件:當(dāng) location.hash 發(fā)生改變時(shí),將觸發(fā)這個(gè)事件

比如訪問一個(gè)路徑 http://sherlocked93.club/base/#/page1,那么上面幾個(gè)值分別為:

# http://sherlocked93.club/base/#/page1
{
  "href": "http://sherlocked93.club/base/#/page1",
  "pathname": "/base/",
  "hash": "#/page1"
}

注意: Hash 方法是利用了相當(dāng)于頁面錨點(diǎn)的功能,所以與原來的通過錨點(diǎn)定位來進(jìn)行頁面滾動(dòng)定位的方式?jīng)_突,導(dǎo)致定位到錯(cuò)誤的路由路徑,因此需要采用別的辦法,之前在寫 progress-catalog 這個(gè)插件碰到了這個(gè)情況。

1.2 實(shí)例

這里簡單做一個(gè)實(shí)現(xiàn),原理是把目標(biāo)路由和對應(yīng)的回調(diào)記錄下來,點(diǎn)擊跳轉(zhuǎn)觸發(fā) hashchange 的時(shí)候獲取當(dāng)前路徑并執(zhí)行對應(yīng)回調(diào),效果:

class RouterClass {
  constructor() {
    this.routes = {}        // 記錄路徑標(biāo)識(shí)符對應(yīng)的cb
    this.currentUrl = ""    // 記錄hash只為方便執(zhí)行cb
    window.addEventListener("load", () => this.render())
    window.addEventListener("hashchange", () => this.render())
  }
  
  /* 初始化 */
  static init() {
    window.Router = new RouterClass()
  }
  
  /* 注冊路由和回調(diào) */
  route(path, cb) {
    this.routes[path] = cb || function() {}
  }
  
  /* 記錄當(dāng)前hash,執(zhí)行cb */
  render() {
    this.currentUrl = location.hash.slice(1) || "/"
    this.routes[this.currentUrl]()
  }
}

具體實(shí)現(xiàn)參照 CodePen

如果希望使用腳本來控制 Hash 路由的后退,可以將經(jīng)歷的路由記錄下來,路由后退跳轉(zhuǎn)的實(shí)現(xiàn)是對 location.hash 進(jìn)行賦值。但是這樣會(huì)引發(fā)重新引發(fā) hashchange 事件,第二次進(jìn)入 render 。所以我們需要增加一個(gè)標(biāo)志位,來標(biāo)明進(jìn)入 render 方法是因?yàn)榛赝诉M(jìn)入的還是用戶跳轉(zhuǎn)

class RouterClass {
  constructor() {
    this.isBack = false
    this.routes = {}        // 記錄路徑標(biāo)識(shí)符對應(yīng)的cb
    this.currentUrl = ""    // 記錄hash只為方便執(zhí)行cb
    this.historyStack = []  // hash棧
    window.addEventListener("load", () => this.render())
    window.addEventListener("hashchange", () => this.render())
  }
  
  /* 初始化 */
  static init() {
    window.Router = new RouterClass()
  }
  
  /* 記錄path對應(yīng)cb */
  route(path, cb) {
    this.routes[path] = cb || function() {}
  }
  
  /* 入棧當(dāng)前hash,執(zhí)行cb */
  render() {
    if (this.isBack) {      // 如果是由backoff進(jìn)入,則置false之后return
      this.isBack = false   // 其他操作在backoff方法中已經(jīng)做了
      return
    }
    this.currentUrl = location.hash.slice(1) || "/"
    this.historyStack.push(this.currentUrl)
    this.routes[this.currentUrl]()
  }
  
  /* 路由后退 */
  back() {
    this.isBack = true
    this.historyStack.pop()                   // 移除當(dāng)前hash,回退到上一個(gè)
    const { length } = this.historyStack
    if (!length) return
    let prev = this.historyStack[length - 1]  // 拿到要回退到的目標(biāo)hash
    location.hash = `#${ prev }`
    this.currentUrl = prev
    this.routes[prev]()                       // 執(zhí)行對應(yīng)cb
  }
}

代碼實(shí)現(xiàn)參考 CodePen

2. HTML5 History Api 2.1 相關(guān) Api

HTML5 提供了一些路由操作的 Api,關(guān)于使用可以參看 這篇 MDN 上的文章,這里就列舉一下常用 Api 和他們的作用,具體參數(shù)什么的就不介紹了,MDN 上都有

history.go(n):路由跳轉(zhuǎn),比如n為 2 是往前移動(dòng)2個(gè)頁面,n為 -2 是向后移動(dòng)2個(gè)頁面,n為0是刷新頁面

history.back():路由后退,相當(dāng)于 history.go(-1)

history.forward():路由前進(jìn),相當(dāng)于 history.go(1)

history.pushState():添加一條路由歷史記錄,如果設(shè)置跨域網(wǎng)址則報(bào)錯(cuò)

history.replaceState():替換當(dāng)前頁在路由歷史記錄的信息

popstate 事件:當(dāng)活動(dòng)的歷史記錄發(fā)生變化,就會(huì)觸發(fā) popstate 事件,在點(diǎn)擊瀏覽器的前進(jìn)后退按鈕或者調(diào)用上面前三個(gè)方法的時(shí)候也會(huì)觸發(fā),參見 MDN

2.2 實(shí)例

將之前的例子改造一下,在需要路由跳轉(zhuǎn)的地方使用 history.pushState 來入棧并記錄 cb,前進(jìn)后退的時(shí)候監(jiān)聽 popstate 事件拿到之前傳給 pushState 的參數(shù)并執(zhí)行對應(yīng) cb,因?yàn)榻栌昧藶g覽器自己的 Api,因此代碼看起來整潔不少

class RouterClass {
  constructor(path) {
    this.routes = {}        // 記錄路徑標(biāo)識(shí)符對應(yīng)的cb
    history.replaceState({ path }, null, path)    // 進(jìn)入狀態(tài)
    this.routes[path] && this.routes[path]()
    window.addEventListener("popstate", e => {
      const path = e.state && e.state.path
      this.routes[path] && this.routes[path]()
    })
  }
  
  /* 初始化 */
  static init() {
    window.Router = new RouterClass(location.pathname)
  }
  
  /* 注冊路由和回調(diào) */
  route(path, cb) {
    this.routes[path] = cb || function() {}
  }
  
  /* 跳轉(zhuǎn)路由,并觸發(fā)路由對應(yīng)回調(diào) */
  go(path) {
    history.pushState({ path }, null, path)
    this.routes[path] && this.routes[path]()
  }
}

Hash 模式是使用 URL 的 Hash 來模擬一個(gè)完整的 URL,因此當(dāng) URL 改變的時(shí)候頁面并不會(huì)重載。History 模式則會(huì)直接改變 URL,所以在路由跳轉(zhuǎn)的時(shí)候會(huì)丟失一些地址信息,在刷新或直接訪問路由地址的時(shí)候會(huì)匹配不到靜態(tài)資源。因此需要在服務(wù)器上配置一些信息,讓服務(wù)器增加一個(gè)覆蓋所有情況的候選資源,比如跳轉(zhuǎn) index.html 什么的,一般來說是你的 app 依賴的頁面,事實(shí)上 vue-router 等庫也是這么推介的,還提供了常見的服務(wù)器配置。

代碼實(shí)現(xiàn)參考 CodePen

網(wǎng)上的帖子大多深淺不一,甚至有些前后矛盾,在下的文章都是學(xué)習(xí)過程中的總結(jié),如果發(fā)現(xiàn)錯(cuò)誤,歡迎留言指出~

參考:

history | MDN

hashchange | MDN

Manipulating the browser history | MDN

前端路由的基本原理 - 大史不說話

History 對象 -- JavaScript 標(biāo)準(zhǔn)參考教程

PS:歡迎大家關(guān)注我的公眾號【前端下午茶】,一起加油吧~

另外可以加入「前端下午茶交流群」微信群,長按識(shí)別下面二維碼即可加我好友,備注加群,我拉你入群~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101490.html

相關(guān)文章

  • 徹底理清前端單頁面應(yīng)用(SPA)的實(shí)現(xiàn)原理 【精讀源碼】

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...

    xiaodao 評論0 收藏0
  • 徹底理清前端單頁面應(yīng)用(SPA)的實(shí)現(xiàn)原理 【精讀源碼】

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...

    崔曉明 評論0 收藏0
  • 徹底理清前端單頁面應(yīng)用(SPA)的實(shí)現(xiàn)原理 【精讀源碼】

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...

    sunny5541 評論0 收藏0
  • Vue2.0實(shí)用筆記

    摘要:實(shí)例中,可追蹤數(shù)據(jù)發(fā)生變化時(shí),會(huì)開啟一個(gè)隊(duì)列,把變化記錄其中,在下一次事件循環(huán)前,進(jìn)行去重優(yōu)化,然后重新渲染。最早通過實(shí)現(xiàn)了這一需求,通過事件可監(jiān)聽的變化,實(shí)現(xiàn)不同頁面的操作。過濾器的使用通過引入中 1、vue中的過渡、動(dòng)畫效果 單組件()v-enter,v-enter-to,v-enter-active,v-leave,v-leave-to,v-leave-active六種狀態(tài)。(定...

    draveness 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<