Vue Router 進階筆記

1、導航守衛
完整的導航解析流程:vue

  1. 導航被觸發
  2. 在失活的組件裏調用beforeRouteLeave
  3. 調用全局的beforeEach
  4. 在重用的組件裏調用beforeRouteUpdate
  5. 在路由配置裏調用beforeEnter
  6. 解析異步路由組件
  7. 在被激活的組件裏調用beforeRouteEnter
  8. 調用全局的beforeResolve
  9. 導航被確認
  10. 調用全局的afterEach
  11. 觸發DOM更新
  12. 調用beforeRouteEnter守衛中傳給next的回調函數,建立好的組件實例會做爲回調函數的參數傳入。

參數或查詢的改變並不會觸發進入/離開的導航守衛。能夠經過觀察$route對象來應對這些變化,或使用beforeRouteUpdate的組件內守衛。
一、全局前置守衛
router.beforeEachwebpack

const router = new VueRouter({...})
router.beforeEach(to,from,next) => {
  /*to:Route--即將要進入的目標路由對象*/
  /*from:Route--當前導航正要離開的路由*/
  /*next:function--必需要調用該方法來resolve這個鉤子。執行效果依賴next方法的調用參數。*/
  next()  /*進行管道中的下一個鉤子,若所有執行完了,則導航的狀態就是confirmed。*/
  next(false)  /*中斷當前的導航。若是瀏覽器的url改變了(多是用戶手動或瀏覽器後退),那麼url會重置到from路由對應的地址。*/
  next('/')  /*同下*/
  next({path:'/'})  /*跳轉到一個不一樣的地址。*/
  next(error)  /*導航被終止,且錯誤會傳遞給router.onError()註冊過的回調。*/
}

確保next函數在任何給定的導航守衛中都被嚴格調用一次。可出現多於一次,但只能在全部的邏輯路徑都不重疊的狀況下,不然鉤子永遠都不會被解析或報錯。web

/* BAD */
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  /*若是用戶未能驗證身份,則 `next` 會被調用兩次*/
  next()
})

/* GOOD */
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

二、全局解析守衛
router.beforeResolve--與router.beforeEachj相似,區別爲:在導航被確認以前,同時在全部組件內守衛和異步路由組件被解析以後,解析守衛就被調用。
三、全局後置鉤子
router.afterEach()--無next(),也不會改變導航自己vuex

router.afterEach(to,from)=>{
  //...
}

路由配置方式:
一、路由獨享的守衛--直接在路由配置上定義beforeEnter守衛promise

const router = new VueRouter({
  routes: [
    {
      path: '/user',
      component: User,
      beforeEnter: (to,from,next)=>{}
    }
  ]
})

二、組件內的守衛
能夠在路由組件內直接定義如下路由導航守衛
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave瀏覽器

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    /* 在渲染該組件的對應路由被 confirm 前調用*/
    /*不能!獲取組件實例 `this`*/
    /*由於當守衛執行前,組件實例還沒被建立,能夠經過傳一個回調給next來訪問組件實例,僅該守衛支持此方法*/
    next(vm => {
      /*經過vm訪問組件實例*/
    })  
  },
  beforeRouteUpdate(to, from, next) {
    /* 在當前路由改變,可是該組件被複用時調用*/
    /* 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候*/
    /* 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。*/
    /* 能夠訪問組件實例 `this`*/
    this.name = to.params.name
    next()
  },
  beforeRouteLeave(to, from, next) {
    /* 導航離開該組件的對應路由時調用*/
    /* 能夠訪問組件實例 `this`*/
    /*該守衛能夠用來禁止用戶在還未保存修改前忽然離開,導航能夠經過next(false)取消*/
    const answer = window.confirm('尚未保存,肯定要離開嗎')
    if(answer){
      next()
    }else{
      next(false)
    }
  }
}

2、路由元信息-meta服務器

路由登陸攔截異步

/*一、在須要作登陸驗證的路由中設置meta*/
requireAuth值爲true
const router = new VueRouter({
  routes = [
    {
      path: '/detail',
      name: 'detail',
      component: Detail,
      meta: {
        requireAuth: true
      }
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    }
  ]
})

/*二、router.beforeEach()鉤子函數,會在進入每一個網頁以前被調用,可在該鉤子函數內作路由攔截*/
router.beforeEach((to,from,next) => {
  /*判斷將進入的路由是否須要路由攔截*/
  if(to.matched.some(record => record.meta.requireAuth){
    /*vuex.state判斷token是否存在*/
    if(store.state.token){
      /*已登陸*/
      next()
    }else{
      next({
        path: '/login',
        query: {redirect: to.fullPath}
      })
    }
  } else {
    next()
  }
})

3、過渡動效
使用<transition>標籤將<router-view>標籤包裹能夠添加一些過渡效果,會給在這個router-view中的路由都加上同一種效果。
若想給不一樣的路由加不一樣的效果,能夠在路由組件中設置<transition>添加效果ide

const Foo = {
  template: `
    <transition name="slide">
      <div class="foo">...</div>
    </transition>
  `
}

const Bar = {
  template: `
    <transition name="fade">
      <div class="bar">...</div>
    </transition>
  `
}

設置動態過渡函數

<!-- 使用動態的 transition name -->
<transition :name="transitionName">
  <router-view></router-view>
</transition>

//...
// 接着在父組件內
// watch $route 決定使用哪一種過渡
watch: {
  '$route' (to, from) {
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
  }
}

4、數據獲取
在進入某個路由後,須要從服務器獲取數據,有兩種方式:
一、導航完成以後獲取:
在路由組件的created鉤子函數中調用獲取數據的方法

export default{
  data (){
    return {
      loading: false
    }
  },
  created(){
    //組件建立完成後獲取數據,此時data已經被observed了
    this.fetchData()
  },
  methods: {
    fetchData() {
      this.loading = true
      //發起數據請求,此時能夠經過$route.params獲取url上的數據
      getPost(this.$route.params.id,(res)=>{
        //...
      })
    }
  }
}

二、在導航前獲取數據
在要跳轉的路由組件中的beforeRouteEnter守衛中獲取數據,當數據獲取成功後只調用next()方法。

export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改變前,組件就已經渲染完了
  // 邏輯稍稍不一樣
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}

5、滾動行爲
當切換到新路由時,頁面滾動到頂部或保持原先的滾動位置,能夠在router實例中提供一個scrollBehavior方法。

const router = new VueRouter({
  routes: [...],
  scrollBehavior(to,from,savedPosition){
    //return 指望滾動到的位置
    //return {x: number, y: number}
    //滾動到頂部
    //return {x:0,y:0}
    
    //savedPosition在按下後退和前進按鈕時可用
    if(savedPosition) {
      return savedPosition
    } else {
      return {x:0,y:0}
    }
  }
})

模擬滾動到錨點

scrollBehavior(to,from,savedPosition){
  if(to.hash) {
    return {
      selector: to.hash,
      //設置平滑滾動
      behavior: 'smooth'
    }
  }
}

6、路由懶加載
爲提升加載效率,使用懶加載,讓路由被訪問時才加載對應組件。需結合Vue異步組件和webpack

//一、將異步組件定義爲返回一個promise的工廠函數。
const Foo = ()=>{
  Promise.resolve({
    //組件定義對象
  })
}
//二、使用import引入組件
import('./Foo.vue')  //import返回一個promise

//可結合二者
const Foo = ()=> import('./Foo.vue')
//路由在實例化時跟普通的同樣
相關文章
相關標籤/搜索