最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度解析Vue中的路由跳轉(zhuǎn)$router.push?VS?location.href

 更新時(shí)間:2026年03月13日 09:10:32   作者:木易?士心  
在?Vue?單頁(yè)應(yīng)用(SPA)中,頁(yè)面跳轉(zhuǎn)是一個(gè)看似簡(jiǎn)單實(shí)則暗藏玄機(jī)的話題,本文將從底層原理、架構(gòu)設(shè)計(jì)、源碼實(shí)現(xiàn)等多個(gè)維度進(jìn)行深度剖析,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

在 Vue 單頁(yè)應(yīng)用(SPA)中,頁(yè)面跳轉(zhuǎn)是一個(gè)看似簡(jiǎn)單實(shí)則暗藏玄機(jī)的話題。this.$router.push('/login')window.location.href = '/#/login' 表面上都能實(shí)現(xiàn)"跳轉(zhuǎn)到登錄頁(yè)"的效果,但其背后的實(shí)現(xiàn)機(jī)制、對(duì)應(yīng)用狀態(tài)的影響、以及在不同場(chǎng)景下的行為差異,直接關(guān)系到應(yīng)用的穩(wěn)定性、用戶體驗(yàn)和可維護(hù)性。

本文將從底層原理、架構(gòu)設(shè)計(jì)源碼實(shí)現(xiàn)等多個(gè)維度進(jìn)行深度剖析。

一、架構(gòu)層面的根本差異

1.1 整體架構(gòu)對(duì)比圖

┌─────────────────────────────────────────────────────────────────────┐
│                         Vue 應(yīng)用架構(gòu)層面                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │              this.$router.push('/login')                     │ │
│   │                                                              │ │
│   │   Vue Component ──? Vue Router ──? Route Matcher            │ │
│   │         │                 │               │                  │ │
│   │         │                 ▼               ▼                  │ │
│   │         │         Navigation Guards   Component              │ │
│   │         │              │            Lifecycle                │ │
│   │         │              ▼               │                     │ │
│   │         │         [權(quán)限校驗(yàn)]          ▼                      │ │
│   │         │         [數(shù)據(jù)預(yù)取]      DOM 更新                   │ │
│   │         │         [過渡動(dòng)畫]    (無刷新)                     │ │
│   │         ▼                                                  │ │
│   │    保持應(yīng)用狀態(tài)、Vuex store、事件監(jiān)聽器等                    │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   ══════════════════════════════════════════════════════════════   │
│                        分 割 線                                     │
│   ══════════════════════════════════════════════════════════════   │
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │           window.location.href = '/#/login'                  │ │
│   │                                                              │ │
│   │   Vue App ──?──? Browser Navigation ──? Page Reload?        │ │
│   │                      │                     │                 │ │
│   │                      ▼                     ▼                 │ │
│   │              直接修改 URL            可能觸發(fā):              │ │
│   │              繞過 Vue Router        - 完整頁(yè)面刷新           │ │
│   │                                      - 重新下載資源          │ │
│   │                                      - 丟失應(yīng)用狀態(tài)          │ │
│   │                                      - 重啟 Vue 實(shí)例         │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.2 核心差異總結(jié)

維度$router.push()location.href
控制權(quán)Vue Router 完全控制瀏覽器原生控制
應(yīng)用狀態(tài)保持完整可能丟失
代碼執(zhí)行在 Vue 上下文中脫離 Vue 上下文
可預(yù)測(cè)性高(遵循路由配置)低(依賴瀏覽器行為)

二、Vue Router 的底層實(shí)現(xiàn)原理

2.1 Vue Router 架構(gòu)圖

┌────────────────────────────────────────────────────────────────────────┐
│                        Vue Router 內(nèi)部架構(gòu)                              │
├────────────────────────────────────────────────────────────────────────┤
│                                                                        │
│  ┌──────────────┐      ┌──────────────┐      ┌──────────────┐        │
│  │   Router     │      │    Route     │      │   Matcher    │        │
│  │   Instance   │?────?│   Matcher    │?────?│   (路由表)    │        │
│  └──────────────┘      └──────────────┘      └──────────────┘        │
│         │                      │                      │               │
│         │                      ▼                      ▼               │
│         │             ┌──────────────────┐   ┌─────────────────┐     │
│         │             │ Navigation       │   │ Route Record    │     │
│         │             │ Guards           │   │ { path, comp,   │     │
│         │             │ (beforeEach等)   │   │  children... }  │     │
│         │             └──────────────────┘   └─────────────────┘     │
│         │                      │                                      │
│         ▼                      ▼                                      │
│  ┌─────────────────────────────────────────────────────────┐         │
│  │                    History Mode                          │         │
│  │  ┌──────────────┐           ┌──────────────┐            │         │
│  │  │ Hash Mode    │           │ History Mode │            │         │
│  │  │ (hashchange) │           │ (pushState)  │            │         │
│  │  └──────────────┘           └──────────────┘            │         │
│  └─────────────────────────────────────────────────────────┘         │
│                                  │                                    │
│                                  ▼                                    │
│                        ┌──────────────────┐                          │
│                        │  View Updating   │                          │
│                        │  (<router-view>) │                          │
│                        └──────────────────┘                          │
│                                                                        │
└────────────────────────────────────────────────────────────────────────┘

2.2$router.push()的完整執(zhí)行流程

┌─────────────────────────────────────────────────────────────────────┐
│                 $router.push() 完整執(zhí)行流程                          │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  1. 參數(shù)標(biāo)準(zhǔn)化                           │
        │     push('/login') 或                    │
        │     push({ path: '/login', query: {} })  │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  2. 路由匹配                  │
        │     - 根據(jù)配置的 routes 數(shù)組匹配          │
        │     - 解析動(dòng)態(tài)路由參數(shù)                   │
        │     - 處理嵌套路由                       │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  3. 觸發(fā)導(dǎo)航守衛(wèi)                         │
        │  ┌────────────────────────────────────┐  │
        │  │ 3.1 失活組件的 beforeRouteLeave    │  │
        │  └────────────────────────────────────┘  │
        │                  │                       │
        │                  ▼                       │
        │  ┌────────────────────────────────────┐  │
        │  │ 3.2 全局 beforeEach               │  │
        │  │     (權(quán)限校驗(yàn)、登錄攔截等)         │  │
        │  └────────────────────────────────────┘  │
        │                  │                       │
        │                  ▼                       │
        │  ┌────────────────────────────────────┐  │
        │  │ 3.3 重用組件的 beforeRouteUpdate   │  │
        │  └────────────────────────────────────┘  │
        │                  │                       │
        │                  ▼                       │
        │  ┌────────────────────────────────────┐  │
        │  │ 3.4 路由配置的 beforeEnter         │  │
        │  └────────────────────────────────────┘  │
        │                  │                       │
        │                  ▼                       │
        │  ┌────────────────────────────────────┐  │
        │  │ 3.5 激活組件的 beforeRouteEnter    │  │
        │  └────────────────────────────────────┘  │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  4. 導(dǎo)航確認(rèn)                             │
        │     - 所有守衛(wèi)都調(diào)用 next() 才會(huì)繼續(xù)     │
        │     - 任一守衛(wèi)調(diào)用 next(false) 則取消    │
        │     - 可重定向到其他路由                 │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  5. 更新 URL (根據(jù)路由模式)              │
        │  ┌────────────────────────────────────┐  │
        │  │ Hash Mode:                        │  │
        │  │   window.location.hash = '#/login'│  │
        │  │   (觸發(fā) hashchange 事件)           │  │
        │  └────────────────────────────────────┘  │
        │  ┌────────────────────────────────────┐  │
        │  │ History Mode:                     │  │
        │  │   history.pushState({}, '', url)  │  │
        │  │   (不觸發(fā)頁(yè)面刷新)                 │  │
        │  └────────────────────────────────────┘  │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  6. 更新應(yīng)用狀態(tài)                         │
        │     - 更新 this.$route 響應(yīng)式對(duì)象        │
        │     - 觸發(fā) <router-view> 重新渲染        │
        │     - 執(zhí)行組件生命周期鉤子               │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  7. 執(zhí)行 afterEach 鉤子                  │
        │     (可用于分析、進(jìn)度條結(jié)束等)           │
        └──────────────────────────────────────────┘
                                │
                                ▼
                        ┌──────────────┐
                        │   導(dǎo)航完成   │
                        └──────────────┘

2.3 源碼級(jí)別的關(guān)鍵實(shí)現(xiàn)

// Vue Router 簡(jiǎn)化版源碼示意
class VueRouter {
  push(location, onComplete, onAbort) {
    // 1. 標(biāo)準(zhǔn)化 location 參數(shù)
    location = normalizeLocation(location, this.currentRoute);
    
    // 2. 匹配路由記錄
    const route = this.matcher.match(location);
    
    // 3. 確認(rèn)導(dǎo)航(執(zhí)行導(dǎo)航守衛(wèi))
    this.confirmTransition(route, () => {
      // 4. 更新當(dāng)前路由
      this.updateRoute(route);
      
      // 5. 更新 URL
      this.ensureURL();
      
      // 6. 觸發(fā)回調(diào)
      onComplete && onComplete(route);
    }, onAbort);
  }
  
  confirmTransition(route, onComplete, onAbort) {
    // 提取所有需要執(zhí)行的導(dǎo)航守衛(wèi)
    const queue = [].concat(
      // 失活組件的 beforeRouteLeave
      extractLeaveGuards(this.currentRoute),
      // 全局 beforeEach
      this.router.beforeHooks,
      // 重用組件的 beforeRouteUpdate
      extractUpdateHooks(this.currentRoute),
      // 路由配置的 beforeEnter
      route.matched.flatMap(m => m.beforeEnter),
      // 激活組件的 beforeRouteEnter
      extractEnterGuards(route)
    );
    
    // 順序執(zhí)行守衛(wèi)
    runQueue(queue, (guard, next) => {
      guard(route, this.currentRoute, (to) => {
        if (to === false) {
          // 取消導(dǎo)航
          onAbort();
        } else if (isRoute(to)) {
          // 重定向
          this.push(to);
        } else {
          // 繼續(xù)下一個(gè)守衛(wèi)
          next();
        }
      });
    }, onComplete);
  }
}

三、Hash 模式 vs History 模式深度解析

3.1 Hash 模式原理

┌─────────────────────────────────────────────────────────────────────┐
│                       Hash 模式工作原理                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   URL 結(jié)構(gòu): https://example.com/#/user/profile?id=123              │
│             └────────────────────┘ └──────────────────────┘       │
│                   基礎(chǔ) URL              hash 部分                  │
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │  特點(diǎn):                                                        │ │
│   │  1. hash 變化不會(huì)觸發(fā)頁(yè)面刷新                                 │ │
│   │  2. hash 不屬于 URL 路徑,不會(huì)被發(fā)送到服務(wù)器                  │ │
│   │  3. 通過 hashchange 事件監(jiān)聽變化                              │ │
│   │  4. 兼容性好,無需服務(wù)器配置                                  │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   事件監(jiān)聽流程:                                                     │
│   ┌──────────┐    hash變化    ┌─────────────┐    觸發(fā)     ┌─────┐│
│   │ 瀏覽器   │ ─────────────? │ hashchange  │ ──────────? │Vue  ││
│   │ 地址欄   │                │   事件      │             │Router││
│   └──────────┘                └─────────────┘             └─────┘│
│                                                                     │
│   Vue Router 初始化:                                                │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ window.addEventListener('hashchange', () => {                │ │
│   │   this.transitionTo(window.location.hash.slice(1));          │ │
│   │ });                                                          │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 History 模式原理

┌─────────────────────────────────────────────────────────────────────┐
│                     History 模式工作原理                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   URL 結(jié)構(gòu): https://example.com/user/profile?id=123                │
│             └────────────────────────┘└──────────────────┘         │
│                    完整路徑都是真實(shí) URL                              │
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │  核心API:                                                     │ │
│   │  - history.pushState(state, title, url)  // 添加歷史記錄     │ │
│   │  - history.replaceState(state, title, url) // 替換當(dāng)前記錄   │ │
│   │  - popstate 事件  // 監(jiān)聽前進(jìn)/后退                           │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │  關(guān)鍵特點(diǎn):                                                    │ │
│   │  1. URL 更美觀,沒有 # 號(hào)                                    │ │
│   │  2. 需要服務(wù)器配置支持(所有路徑都返回 index.html)          │ │
│   │  3. pushState/replaceState 不會(huì)觸發(fā)頁(yè)面刷新                  │ │
│   │  4. popstate 事件僅在瀏覽器前進(jìn)/后退時(shí)觸發(fā)                   │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   服務(wù)器配置示例:                                                   │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ server {                                                     │ │
│   │   location / {                                               │ │
│   │     try_files $uri $uri/ /index.html;                        │ │
│   │   }                                                          │ │
│   │ }                                                            │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   ?  致命陷阱:                                                     │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ 如果在 History 模式下使用:                                    │ │
│   │   window.location.href = '/#/login'                         │ │
│   │                                                              │ │
│   │ 實(shí)際會(huì)跳轉(zhuǎn)到: https://example.com/#/login                    │ │
│   │ 這與你的 History 模式路由 /login 完全不匹配!                │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.3 兩種模式的路由模式選擇決策樹

                        開始選擇路由模式
                              │
                              ▼
                    ┌─────────────────┐
                    │ 是否需要美觀URL │
                    │   (無 # 號(hào))?    │
                    └─────────────────┘
                              │
                 ┌────────────┴────────────┐
                 │                         │
                 ▼                         ▼
                否                         是
                 │                         │
                 ▼                         ▼
        ┌──────────────┐         ┌─────────────────┐
        │  Hash Mode   │         │ 是否能控制      │
        │  (簡(jiǎn)單省事)  │         │ 服務(wù)器配置?    │
        └──────────────┘         └─────────────────┘
                                          │
                            ┌─────────────┴─────────────┐
                            │                           │
                            ▼                           ▼
                           否                          是
                            │                           │
                            ▼                           ▼
                  ┌──────────────────┐        ┌──────────────────┐
                  │   Hash Mode      │        │  History Mode    │
                  │ (無服務(wù)器權(quán)限時(shí)) │        │  (最佳體驗(yàn))      │
                  └──────────────────┘        └──────────────────┘

四、window.location.href的執(zhí)行機(jī)制

4.1 瀏覽器導(dǎo)航流程

┌─────────────────────────────────────────────────────────────────────┐
│              window.location.href = '/login' 執(zhí)行流程               │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  1. 瀏覽器解析新 URL                     │
        │     - 解析協(xié)議、域名、端口、路徑         │
        └──────────────────────────────────────────┘
                                │
                                ▼
        ┌──────────────────────────────────────────┐
        │  2. 判斷是否需要刷新頁(yè)面                 │
        │  ┌────────────────────────────────────┐  │
        │  │ 需要刷新的情況:                    │  │
        │  │ - 協(xié)議不同       │  │
        │  │ - 域名/端口不同                    │  │
        │  │ - 完整路徑變化(非 hash 部分)     │  │
        │  └────────────────────────────────────┘  │
        │  ┌────────────────────────────────────┐  │
        │  │ 不刷新的情況:                      │  │
        │  │ - 僅 hash 部分變化                 │  │
        │  │   (如 /page → /page#section)      │  │
        │  └────────────────────────────────────┘  │
        └──────────────────────────────────────────┘
                                │
                    ┌───────────┴───────────┐
                    │                       │
                    ▼                       ▼
            需要刷新                僅 Hash 變化
                    │                       │
                    ▼                       ▼
    ┌────────────────────────┐    ┌─────────────────────┐
    │  3a. 完整頁(yè)面刷新      │    │  3b. 僅滾動(dòng)到錨點(diǎn)   │
    │  ┌──────────────────┐  │    │  或觸發(fā) hashchange │
    │  │ - 卸載當(dāng)前頁(yè)面   │  │    │                     │
    │  │ - 清除所有狀態(tài)   │  │    │  ? Vue Router      │
    │  │   (Vuex、事件等) │  │    │  無法感知此變化!   │
    │  │ - 發(fā)起新HTTP請(qǐng)求 │  │    │                     │
    │  │ - 重新加載資源   │  │    └─────────────────────┘
    │  │ - 重新初始化 Vue │  │
    │  └──────────────────┘  │
    └────────────────────────┘
                    │
                    ▼
        ┌──────────────────────────────────────────┐
        │  4. 更新瀏覽器歷史記錄                   │
        │     (可通過后退按鈕返回)                 │
        └──────────────────────────────────────────┘

4.2 關(guān)鍵問題:繞過 Vue Router 的后果

場(chǎng)景:History 模式下執(zhí)行

window.location.href = '/login'

問題分析:

瀏覽器行為: 

1. 檢測(cè)到完整路徑變化(從 /home 到 /login)

2. 觸發(fā)頁(yè)面刷新

3. 向服務(wù)器請(qǐng)求 /login 資源

服務(wù)器響應(yīng): 

  • 如果配置了 SPA fallback → 返回 index.html → Vue 重新啟動(dòng)
  • 如果未配置 → 返回 404 

Vue 應(yīng)用狀態(tài): 

  • Vuex store 被重置
  • 所有組件實(shí)例銷毀 
  • 事件監(jiān)聽器清除

定時(shí)器、WebSocket 等需要重新建立

五、導(dǎo)航守衛(wèi)完整流程圖

5.1 導(dǎo)航守衛(wèi)執(zhí)行順序

┌─────────────────────────────────────────────────────────────────────┐
│                     導(dǎo)航守衛(wèi)完整執(zhí)行順序                             │
│                  (從 /home 導(dǎo)航到 /user/profile)                    │
└─────────────────────────────────────────────────────────────────────┘
    觸發(fā)導(dǎo)航: this.$router.push('/user/profile')
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段一:離開當(dāng)前路由】                                             │
│                                                                     │
│   1. beforeRouteLeave (Home組件內(nèi))                                 │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ 用途:防止用戶未保存就離開、清理定時(shí)器等                   │  │
│      │ 示例:                                                    │  │
│      │   beforeRouteLeave(to, from, next) {                     │  │
│      │     if (this.hasUnsavedChanges) {                        │  │
│      │       const confirm = window.confirm('確定離開?');      │  │
│      │       if (!confirm) return next(false);                  │  │
│      │     }                                                    │  │
│      │     next();                                              │  │
│      │   }                                                      │  │
│      └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段二:全局前置守衛(wèi)】                                             │
│                                                                     │
│   2. beforeEach (全局注冊(cè))                                         │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ 用途:權(quán)限驗(yàn)證、登錄檢查、路由攔截                        │  │
│      │ 示例:                                                    │  │
│      │   router.beforeEach((to, from, next) => {                │  │
│      │     const isLoggedIn = store.state.isLoggedIn;           │  │
│      │     if (to.meta.requiresAuth && !isLoggedIn) {           │  │
│      │       next({ path: '/login', query: { redirect: to.fullPath } });│
│      │     } else {                                             │  │
│      │       next();                                            │  │
│      │     }                                                    │  │
│      │   });                                                    │  │
│      └──────────────────────────────────────────────────────────┘  │
│                                                                     │
│   3. beforeResolve (全局,在所有組件內(nèi)守衛(wèi)之后)                    │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ 用途:導(dǎo)航確認(rèn)前的最后檢查                                │  │
│      └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段三:路由獨(dú)享守衛(wèi)】                                             │
│                                                                     │
│   4. beforeEnter (路由配置中)                                      │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ 用途:特定路由的權(quán)限控制、數(shù)據(jù)預(yù)加載                      │  │
│      │ 示例:                                                    │  │
│      │   {                                                      │  │
│      │     path: '/admin',                                      │  │
│      │     component: Admin,                                    │  │
│      │     beforeEnter: (to, from, next) => {                   │  │
│      │       if (store.state.user.role !== 'admin') {           │  │
│      │         next('/403');                                    │  │
│      │       } else {                                           │  │
│      │         next();                                          │  │
│      │       }                                                  │  │
│      │     }                                                    │  │
│      │   }                                                      │  │
│      └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段四:組件內(nèi)守衛(wèi)】                                               │
│                                                                     │
│   5. beforeRouteEnter (目標(biāo)組件 User)                              │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ ? 注意:此時(shí)組件實(shí)例還未創(chuàng)建,無法訪問 this             │  │
│      │ 用途:獲取路由數(shù)據(jù),通過回調(diào)訪問組件實(shí)例                  │  │
│      │ 示例:                                                    │  │
│      │   beforeRouteEnter(to, from, next) {                     │  │
│      │     // ? 錯(cuò)誤:this 未定義                              │  │
│      │     // this.loadUser();                                  │  │
│      │                                                          │  │
│      │     // ? 正確:通過回調(diào)訪問實(shí)例                         │  │
│      │     next(vm => {                                         │  │
│      │       vm.loadUser(to.params.id);                         │  │
│      │     });                                                  │  │
│      │   }                                                      │  │
│      └──────────────────────────────────────────────────────────┘  │
│                                                                     │
│   6. beforeRouteUpdate (當(dāng)路由參數(shù)變化時(shí)復(fù)用組件)                  │
│      ┌──────────────────────────────────────────────────────────┐  │
│      │ 場(chǎng)景:/user/1 → /user/2 (同一個(gè) User 組件)              │  │
│      │ 示例:                                                    │  │
│      │   beforeRouteUpdate(to, from, next) {                    │  │
│      │     this.loadUser(to.params.id);                         │  │
│      │     next();                                              │  │
│      │   }                                                      │  │
│      └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段五:導(dǎo)航確認(rèn)】                                                 │
│                                                                     │
│   所有守衛(wèi)都調(diào)用 next() → 導(dǎo)航確認(rèn)                                  │
│   任一守衛(wèi)調(diào)用 next(false) → 導(dǎo)航取消                               │
│   任一守衛(wèi)調(diào)用 next('/path') → 重定向                               │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段六:更新視圖】                                                 │
│                                                                     │
│   1. 失活 Home 組件 → beforeDestroy → destroyed                    │
│   2. 激活 User 組件 → beforeCreate → created → beforeMount → mounted│
│   3. 更新 <router-view>                                            │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 【階段七:全局后置鉤子】                                             │
│                                                                     │
│   afterEach (全局注冊(cè))                                             │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ 用途:進(jìn)度條結(jié)束、頁(yè)面標(biāo)題設(shè)置、埋點(diǎn)統(tǒng)計(jì)                     │ │
│   │ 示例:                                                       │ │
│   │   router.afterEach((to, from) => {                          │ │
│   │     NProgress.done();                                       │ │
│   │     document.title = to.meta.title || 'My App';             │ │
│   │     analytics.trackPageView(to.fullPath);                   │ │
│   │   });                                                       │ │
│   └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
         │
         ▼
       導(dǎo)航完成

5.2 守衛(wèi)對(duì)比:$router.pushvslocation.href

┌─────────────────────────────────────────────────────────────────────┐
│                    導(dǎo)航守衛(wèi)觸發(fā)對(duì)比                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   this.$router.push('/login')                                      │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ ? beforeRouteLeave    → 觸發(fā)                               │ │
│   │ ? beforeEach          → 觸發(fā)                               │ │
│   │ ? beforeEnter         → 觸發(fā)                               │ │
│   │ ? beforeRouteEnter    → 觸發(fā)                               │ │
│   │ ? afterEach           → 觸發(fā)                               │ │
│   │ ? 組件生命周期         → 正常執(zhí)行                          │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   window.location.href = '/#/login' (Hash 模式)                    │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ ? beforeRouteLeave    → 不觸發(fā)                             │ │
│   │ ? beforeEach          → 不觸發(fā)                             │ │
│   │ ? beforeEnter         → 不觸發(fā)                             │ │
│   │ ? beforeRouteEnter    → 不觸發(fā)                             │ │
│   │ ? afterEach           → 不觸發(fā)                             │ │
│   │ ?  可能觸發(fā) hashchange → Vue Router 可能捕獲到變化         │ │
│   │ ?  組件狀態(tài)            → 可能不一致                         │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
│   window.location.href = '/login' (History 模式)                   │
│   ┌──────────────────────────────────────────────────────────────┐ │
│   │ ? 所有導(dǎo)航守衛(wèi)         → 全部失效                          │ │
│   │ ?? 頁(yè)面完全刷新         → Vue 應(yīng)用重啟                      │ │
│   │ ?? 應(yīng)用狀態(tài)丟失         → Vuex 重置                         │ │
│   │ ?? 內(nèi)存泄漏風(fēng)險(xiǎn)         → 未清理的事件監(jiān)聽器、定時(shí)器        │ │
│   └──────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

六、實(shí)戰(zhàn)場(chǎng)景深度對(duì)比

6.1 場(chǎng)景一:登錄攔截

場(chǎng)景:未登錄用戶訪問需要權(quán)限的頁(yè)面

正確做法:使用 $router.push

// router/index.js                                             
  router.beforeEach((to, from, next) => {                        
    const isLoggedIn = store.state.auth.isLoggedIn;              
    if (to.meta.requiresAuth && !isLoggedIn) {                   
      // 保存目標(biāo)路徑,登錄后重定向                                
      next({                                                      
        path: '/login',                                           
        query: { redirect: to.fullPath }                          
      });                                                         
    } else {                                                      
      next();                                                     
    }                                                             
  });                                                             
  // Login.vue - 登錄成功后                                       
  handleLoginSuccess() {                                          
    const redirect = this.$route.query.redirect || '/dashboard'; 
    this.$router.push(redirect);  // ? 正確                     
  }                                                               

錯(cuò)誤做法:使用 location.href

// ? 錯(cuò)誤示例                                                  
  if (!isLoggedIn) {                                             
    window.location.href = '/#/login';                           
    // 問題:                                                     
    // 1. 無法保存 redirect 參數(shù)                                  
    // 2. Vuex 狀態(tài)可能丟失                                       
    // 3. 導(dǎo)航守衛(wèi)失效,安全檢查被繞過                            
  }        

6.2 場(chǎng)景二:Token 過期處理

場(chǎng)景:API 請(qǐng)求返回 401,需要跳轉(zhuǎn)登錄頁(yè)                   

完整解決方案

 // utils/request.js (axios 攔截器)                              
  import router from '@/router';                                 
  import store from '@/store';                                   
  service.interceptors.response.use(                             
    response => response.data,                                   
    error => {                                                    
      if (error.response?.status === 401) {                      
        // 清除用戶信息                                           
        store.dispatch('auth/logout');                           
        // ? 正確:使用 router 進(jìn)行跳轉(zhuǎn)                          
        router.push({                                             
          path: '/login',                                         
          query: {                                                
            redirect: router.currentRoute.fullPath,               
            message: '登錄已過期,請(qǐng)重新登錄'                      
          }                                                       
        });                                                       
        // ? 錯(cuò)誤示例                                            
        // window.location.href = '/login';                       
        // 會(huì)丟失當(dāng)前的 redirect 信息                             
        // 且 Vuex 狀態(tài)已清空,但應(yīng)用可能未正確重置               
      }                                                           
      return Promise.reject(error);                               
    }                                                             
  );     

6.3 場(chǎng)景三:路由懶加載與代碼分割

場(chǎng)景:路由懶加載的加載時(shí)機(jī)                               

路由配置

const routes = [                                               
    {                                                             
      path: '/admin',                                             
      component: () => import('@/views/Admin.vue'),  // 懶加載   
      meta: { requiresAuth: true }                               
    }                                                             
  ];                                                              

$router.push 的懶加載流程

this.$router.push('/admin')              

1. beforeEach 檢查權(quán)限                   

  • 未登錄 → 重定向到登錄頁(yè)            
  • 已登錄 → 繼續(xù)                      

2. 開始加載 Admin.vue 的 chunk          

  • 顯示加載指示器                      
  • 網(wǎng)絡(luò)請(qǐng)求獲取 JS 文件                

3. chunk 加載完成                        

  • 執(zhí)行 beforeEnter                    
  • 創(chuàng)建組件實(shí)例                        
  • 渲染頁(yè)面                            

location.href 的風(fēng)險(xiǎn)

window.location.href = '/admin'                                

問題:

1. 如果是 History 模式 → 頁(yè)面刷新                               

2. 向服務(wù)器請(qǐng)求 /admin 資源                                      

3. 服務(wù)器可能返回 404(SPA fallback 未配置)                     

4. 即使返回 index.html,也重新下載了所有資源                     

5. 懶加載的優(yōu)勢(shì)被破壞 

七、性能與用戶體驗(yàn)對(duì)比

7.1 性能對(duì)比測(cè)試

性能對(duì)比(模擬數(shù)據(jù))                               

測(cè)試場(chǎng)景:從首頁(yè)跳轉(zhuǎn)到用戶中心(包含 3 個(gè) API 請(qǐng)求)               

this.$router.push('/user')                                     

導(dǎo)航開始到頁(yè)面可交互:                                          

  • 路由守衛(wèi)執(zhí)行:     ~5ms                                     
  • 組件實(shí)例化:       ~10ms                                    
  • DOM 更新:         ~15ms                                    
  • API 請(qǐng)求:         ~200-500ms                               

總計(jì):             ~230-530ms                               

優(yōu)勢(shì):                                                           

  • 無白屏?xí)r間                                                  
  • 可顯示加載狀態(tài)                                              
  • 保持滾動(dòng)位置(可配置)                                      
  • 資源無需重新下載                                            
window.location.href = '/user' (History 模式)                  

頁(yè)面刷新流程:                                                  

  • 卸載舊頁(yè)面:       ~50ms                                    
  • 網(wǎng)絡(luò)請(qǐng)求 HTML:    ~100-300ms                               
  • 解析 HTML:        ~50ms                                    
  • 下載 JS/CSS:      ~200-500ms                               
  • Vue 初始化:       ~50-100ms                                
  • 組件渲染:         ~30-50ms                                 
  • API 請(qǐng)求:         ~200-500ms                               

總計(jì):             ~680-1550ms                              

劣勢(shì):                                                           

  • 明顯白屏?xí)r間                                                
  • 重復(fù)下載資源                                                
  • 丟失應(yīng)用狀態(tài)                                                
  • 滾動(dòng)位置重置                                                                                                                  

性能差距:約 3-5 倍                                               

7.2 用戶體驗(yàn)對(duì)比

用戶體驗(yàn)對(duì)比                                    

this.$router.push()                                               
  • 無白屏,過渡流暢                                             
  • 可顯示頁(yè)面加載進(jìn)度條                                         
  • 支持頁(yè)面切換動(dòng)畫                                 
  • 保持全局狀態(tài)(主題、語(yǔ)言等)                                 
  • 可實(shí)現(xiàn)頁(yè)面緩存功能                        
  • 支持 prefetch 預(yù)加載                                         
window.location.href                                              
  • 明顯的白屏閃爍                                               
  • 瀏覽器原生加載指示器                                         
  • 無過渡動(dòng)畫                                                   
  • 所有狀態(tài)丟失,需重新初始化                                   
  • 用戶需要重新等待所有資源加載                                 
  • 表單數(shù)據(jù)、未保存內(nèi)容丟失                                     

八、常見陷阱與最佳實(shí)踐

8.1 常見陷阱

常見陷阱匯總                                 

陷阱 1:在 History 模式下硬編碼 hash 路徑                            

// ? 錯(cuò)誤:History 模式下跳轉(zhuǎn)到 hash 路徑                           
  window.location.href = '/#/login';                                  
  // 結(jié)果:URL 變成 https://example.com/#/login                       
  // 與路由配置 /login 不匹配,404!                                  
  // ? 正確:使用路由名稱或路徑                                       
  this.$router.push('/login');                                        
  // 或                                                               
  this.$router.push({ name: 'Login' });                               

陷阱 2:在異步回調(diào)中使用 location.href                               

// ? 錯(cuò)誤示例                                                       
  setTimeout(() => {                                                
    window.location.href = '/dashboard';                              
  }, 1000);                                                           
  // 問題:期間用戶可能已經(jīng)導(dǎo)航到其他頁(yè)面,造成混亂                    
  // ? 正確:使用路由導(dǎo)航                                             
  setTimeout(() => {                                                  
    this.$router.push('/dashboard');                                  
  }, 1000);                                                           
  // 或者更好的做法:在導(dǎo)航守衛(wèi)中處理                                  

陷阱 3:忽略路由守衛(wèi)的權(quán)限檢查                                       

// ? 危險(xiǎn):繞過權(quán)限檢查                                             
  if (userClickedAdminLink) {                                         
    window.location.href = '/admin';                                  
  }                                                                   
  // 即使沒有權(quán)限,也能訪問 admin 頁(yè)面(雖然 API 會(huì)拒絕)              
  // ? 正確:通過路由跳轉(zhuǎn),觸發(fā)權(quán)限檢查                               
  if (userClickedAdminLink) {                                         
    this.$router.push('/admin');                                      
    // beforeEach 會(huì)檢查權(quán)限并拒絕導(dǎo)航                                
  }                                                                   

陷阱 4:在新窗口打開鏈接時(shí)誤用路由                                   

// ? 錯(cuò)誤:無法在新窗口打開                                         
  this.$router.push('/help');  // 只能在當(dāng)前窗口打開                  
  // ? 正確:使用原生方式打開新窗口                                   
  const routeData = this.$router.resolve('/help');                    
  window.open(routeData.href, '_blank');                              

8.2 最佳實(shí)踐

最佳實(shí)踐指南                                   

1. 統(tǒng)一使用路由導(dǎo)航                                                  

 // 創(chuàng)建導(dǎo)航工具函數(shù)                                                   
  // utils/navigation.js                                              
  import router from '@/router';                                      
  export function navigateTo(path, query = {}) {                      
    return router.push({ path, query });                              
  }                                                                   
  export function navigateByName(name, params = {}) {                 
    return router.push({ name, params });                             
  }                                                                   
  export function redirectTo(path) {                                  
    return router.replace(path);                                      
  }                                                                   
  export function openInNewTab(path) {                                
    const routeData = router.resolve(path);                           
    window.open(routeData.href, '_blank');                            
  }                                                                   

2. 合理使用路由守衛(wèi)                                                  

// 權(quán)限守衛(wèi)                                                          
  router.beforeEach(async (to, from, next) => {                       
    // 白名單路由直接通過                                              
    if (to.meta.whiteList) return next();                             
    // 檢查登錄狀態(tài)                                                    
    const isLoggedIn = await checkAuthStatus();                       
    if (!isLoggedIn && to.meta.requiresAuth) {                        
      return next({                                                   
        path: '/login',                                               
        query: { redirect: to.fullPath }                              
      });                                                             
    }                                                                 
    // 權(quán)限檢查                                                        
    if (to.meta.roles) {                                              
      const userRole = store.state.user.role;                         
      if (!to.meta.roles.includes(userRole)) {                        
        return next('/403');                                          
      }                                                               
    }                                                                 
    next();                                                           
  });                                                                 

3. 何時(shí)可以使用 location.href                                        

//  合理使用場(chǎng)景:                                                 
  // 場(chǎng)景 1:跳轉(zhuǎn)到外部網(wǎng)站                                            
  window.location.;                 
  // 場(chǎng)景 2:完全重置應(yīng)用狀態(tài)(如退出登錄)                            
  function logout() {                                                 
    clearAllStorage();                                                
    window.location.href = '/login';  // 完全重置應(yīng)用                
  }                                                                   
  // 場(chǎng)景 3:非 Vue 管理的頁(yè)面(如靜態(tài)頁(yè)面)                            
  window.location.href = '/static/about.html';                        
  // 場(chǎng)景 4:處理特定錯(cuò)誤(如 404 頁(yè)面在 Vue 之外)                    
  if (isCriticalError) {                                              
    window.location.href = '/error.html';                             
  }                                                                   

九、總結(jié)與決策矩陣

9.1 完整對(duì)比表

特性this.$router.pushwindow.location.href
頁(yè)面刷新無刷新可能刷新(History模式)
導(dǎo)航守衛(wèi)完整觸發(fā)完全繞過
組件生命周期正常執(zhí)行可能丟失  
應(yīng)用狀態(tài)保持完整可能重置
過渡動(dòng)畫支持不支持
路由懶加載按需加載  可能重復(fù)下載
模式兼容性自動(dòng)適配需手動(dòng)處理
參數(shù)傳遞query/params手動(dòng)拼接 URL  
編程控制Promise 返回無返回值
錯(cuò)誤處理可捕獲錯(cuò)誤無錯(cuò)誤處理
性能高(無重新加載)低(重新加載)
SEO 友好需 SSR原生支持
推薦程度強(qiáng)烈推薦僅限特殊場(chǎng)景

9.2 選擇決策流程圖

                    需要在 Vue 應(yīng)用中跳轉(zhuǎn)頁(yè)面
                              │
                              ▼
                    ┌─────────────────┐
                    │ 是否為 Vue SPA  │
                    │    內(nèi)部路由?    │
                    └─────────────────┘
                              │
                 ┌────────────┴────────────┐
                 │                         │
                是                         否
                 │                         │
                 ▼                         ▼
        ┌──────────────┐         ┌─────────────────┐
        │ 使用         │         │ 跳轉(zhuǎn)到外部網(wǎng)站  │
        │ $router.push │         │ 或非 Vue 頁(yè)面   │
        └──────────────┘         └─────────────────┘
                 │                         │
                 ▼                         ▼
        ┌──────────────────┐      window.location.href
        │ 需要權(quán)限檢查?   │
        └──────────────────┘
                 │
        ┌────────┴────────┐
        │                 │
       是                 否
        │                 │
        ▼                 ▼
 ┌─────────────┐   ┌─────────────┐
 │ beforeEach  │   │ 直接 push   │
 │ 守衛(wèi)攔截    │   └─────────────┘
 └─────────────┘
        │
        ▼
 ┌──────────────────────┐
 │ 需要新窗口打開?     │
 └──────────────────────┘
        │
   ┌────┴────┐
   │         │
  是         否
   │         │
   ▼         ▼
┌────────┐ ┌────────┐
│resolve │ │  push  │
│+ open  │ └────────┘
└────────┘

結(jié)語(yǔ)

this.$router.push()window.location.href 雖然都能實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn),但本質(zhì)上是兩種完全不同的架構(gòu)思路的體現(xiàn):

  • $router.push() 代表了現(xiàn)代 SPA 應(yīng)用的設(shè)計(jì)理念:狀態(tài)驅(qū)動(dòng)、組件化、聲明式,充分利用框架的能力,實(shí)現(xiàn)流暢的用戶體驗(yàn)和可維護(hù)的代碼結(jié)構(gòu)。
  • location.href 則是傳統(tǒng)多頁(yè)應(yīng)用的遺留方式:命令式、頁(yè)面中心、狀態(tài)獨(dú)立,在 SPA 中使用會(huì)破壞應(yīng)用的完整性和用戶體驗(yàn)。

核心建議:在 Vue SPA 中,99% 的場(chǎng)景都應(yīng)該使用 $router.push(),只有在外部跳轉(zhuǎn)、完全重置應(yīng)用等特殊場(chǎng)景下,才考慮使用 location.href。

理解這兩者的區(qū)別,不僅能幫助你寫出更好的代碼,更能讓你深入理解 SPA 架構(gòu)的設(shè)計(jì)哲學(xué)。

以上就是深度解析Vue中的路由跳轉(zhuǎn)$router.push VS location.href的詳細(xì)內(nèi)容,更多關(guān)于Vue路由跳轉(zhuǎn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于ElementUI的el-upload組件二次封裝的問題

    關(guān)于ElementUI的el-upload組件二次封裝的問題

    這篇文章主要介紹了關(guān)于ElementUI的el-upload組件二次封裝的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue elementUI下拉框值無法選中問題及解決方案

    vue elementUI下拉框值無法選中問題及解決方案

    大家在寫系統(tǒng)的時(shí)候,會(huì)有這樣的需求:點(diǎn)擊修改后把需要修改的數(shù)據(jù)放入表單,其中會(huì)有下拉單選框、下拉多選框,展示下拉框單選框內(nèi)的數(shù)據(jù)只需要將所選id賦值給下拉框綁定的值就可以了,下面給大家分享vue elementUI下拉框值無法選中問題,感興趣的朋友一起看看吧
    2024-03-03
  • vue與electron實(shí)現(xiàn)進(jìn)程間的通信詳情

    vue與electron實(shí)現(xiàn)進(jìn)程間的通信詳情

    這篇文章主要介紹了vue與electron實(shí)現(xiàn)進(jìn)程間的通信詳情,本文主要介紹electron渲染進(jìn)程和主進(jìn)程間的通信,以及在渲染進(jìn)程和主進(jìn)程中常用的配置項(xiàng),需要的朋友可以參考一下
    2022-09-09
  • vue 掛載路由到頭部導(dǎo)航的方法

    vue 掛載路由到頭部導(dǎo)航的方法

    本篇文章主要介紹了vue 掛載路由到頭部導(dǎo)航的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • vue修改proxyTable解決跨域請(qǐng)求,報(bào)404的問題及解決

    vue修改proxyTable解決跨域請(qǐng)求,報(bào)404的問題及解決

    這篇文章主要介紹了vue修改proxyTable解決跨域請(qǐng)求,報(bào)404的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Element InputNumber計(jì)數(shù)器的使用方法

    Element InputNumber計(jì)數(shù)器的使用方法

    這篇文章主要介紹了Element InputNumber計(jì)數(shù)器的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Vue自定義指令詳細(xì)

    Vue自定義指令詳細(xì)

    這篇文章主要介紹了Vue自定義指令,文章從背景開始詳細(xì)介紹Vue自定義指令的詳細(xì)內(nèi)容,隨著Vue自定義指令的相關(guān)資料展開具體內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • vue3+element 分片上傳與分片下載功能實(shí)現(xiàn)方法詳解

    vue3+element 分片上傳與分片下載功能實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了vue3+element 分片上傳與分片下載功能實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了vue3+element 分片上傳與下載相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下
    2023-06-06
  • Vue中配置使用process.env詳解

    Vue中配置使用process.env詳解

    process.env?是?Node.js?中的一個(gè)環(huán)境對(duì)象,其中保存著系統(tǒng)的環(huán)境的變量信息,可使用?Node.js?命令行工具直接進(jìn)行查看,這篇文章主要介紹了Vue中配置process.env詳解,需要的朋友可以參考下
    2023-03-03
  • vue項(xiàng)目中如何使用mock你知道嗎

    vue項(xiàng)目中如何使用mock你知道嗎

    這篇文章主要為大家介紹了vue項(xiàng)目如何使用mock,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11

最新評(píng)論

郴州市| 秭归县| 章丘市| 顺义区| 林甸县| 四会市| 驻马店市| 新津县| 银川市| 营口市| 昔阳县| 伊通| 陇西县| 桃园市| 大洼县| 天长市| 兰州市| 西和县| 嘉荫县| 丹凤县| 华蓥市| 平陆县| 昌黎县| 怀宁县| 清远市| 藁城市| 杭锦旗| 阿拉尔市| 密山市| 涿州市| 宁南县| 兴宁市| 仙桃市| 资溪县| 庄河市| 响水县| 澎湖县| 青河县| 台安县| 临沂市| 泸西县|