前端路由: hash & history 模式

胡九筒發表於2018-12-18

一、 history 模式

history原理: 利用H5的 history中新增的兩個API pushState() 和 replaceState() 和一個事件onpopstate

pushState是往history物件裡新增一個新的歷史記錄,即壓棧。 replaceState 是替換history物件中的當前歷史。 這兩個api 接受三個引數:

history.pushState(
    {
        key: 'value'    // 狀態物件
    },
    'title',            // 標題
    'url'               // url
)
複製程式碼

當點選瀏覽器後退按鈕或js呼叫history.back都會觸發onpopstate事件

缺陷:

直接訪問二級頁面或者在頁面內重新整理,會出現404錯誤

解決方法:

後端ngnix中配置重定向到首頁路由上

二、hash模式

原理是監聽onhashchange()事件。僅 hash 符號之前的內容會被包含在請求中,如 www.abc.com,因此對於後端來說,即使沒有做到對路由的全覆蓋,也不會返回 404 錯誤。

三、[vue-router][1]

vue-router預設hash模式

3.1 訪問路由器

this.$router  // 訪問路由器
this.$route   // 訪問當前路由
複製程式碼

3.2 動態路由匹配

const router = new VueRouter({
  routes: [
    // 動態路徑引數 以冒號開頭
    { path: '/user/:id', component: User }
  ]
})

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
複製程式碼

3.3 巢狀路由

要在巢狀的出口中渲染元件,需要在 VueRouter 的引數中使用 children 配置:

const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // 當 /user/:id/profile 匹配成功,
          // UserProfile 會被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 當 /user/:id/posts 匹配成功
          // UserPosts 會被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})
複製程式碼

3.4 程式設計式導航

router.push():向 history 棧新增一個新的記錄 router.replace():替換掉當前的 history 記錄。 router.go(n): 在 history 記錄中向前或者後退多少步 效仿window.history API,且在各類路由模式(history,hash)下表現一致

// 字串
router.push('home')

// 物件
router.push({ path: 'home' })

// 命名的路由(如果提供了 path,params 會被忽略)
router.push({ name: 'user', params: { userId: 123 }})

// 帶查詢引數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
複製程式碼

3.5 導航守衛

全域性守衛

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // to: Route: 即將要進入的目標 路由物件
  // from: Route: 當前導航正要離開的路由
  // next: Function: 一定要呼叫該方法來 resolve 這個鉤子。執行效果依賴 next 方法的呼叫引數
})
複製程式碼

全域性後置鉤子

router.afterEach((to, from) => {
  // ...
})
複製程式碼

3.6 路由元資訊

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

// 訪問meta物件
// 一個路由匹配到的所有路由記錄會暴露為 $route 物件 (還有在導航守衛中的路由物件) 的 $route.matched 陣列。因此,我們需要遍歷 $route.matched 來檢查路由記錄中的 meta 欄位。
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 確保一定要呼叫 next()
  }
})
複製程式碼

3.7 路由懶載入

使用動態 import語法來定義程式碼分塊點 (split point): 如果您使用的是 Babel,你將需要新增 syntax-dynamic-import 外掛,才能使 Babel 可以正確地解析語法。

const router = new VueRouter({
  routes: [
    { path: '/foo', component: () => import('./Foo.vue') }
  ]
})
複製程式碼

相關文章