首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue-router详解及实例

vue-router详解及实例

作者头像
奋飛
发布2019-08-14 18:06:21
2.8K0
发布2019-08-14 18:06:21
举报
文章被收录于专栏:Super 前端Super 前端

写在前面

用户进行了交互操作,现在要对页面内容进行变更,可以通过javascript进行动态替换DOM,但是其不便于分享、收藏,对于搜索引擎和用户来说都是不友好的!

什么是前端路由?

​ 根据不同的 url 地址展示不同的内容或页面,无需依赖服务器根据不同URL进行页面展示操作

优点

  • 用户体验好,不需要每次都从服务器全部获取,快速展现给用户

缺点

  • 使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存
  • 单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

简介

​ 使用 Vue.js ,可以通过组合组件来组成应用程序,当你要把 vue-router 添加进来,我们需要做的是,将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。

// 1. 定义、引用(路由)组件。
const Foo = { template: '<div>foo</div>' }
import Bar from '@/views/bar.vue'

// 2. 定义路由
const routes = [
  { path: '/foo', name: 'foo', component: Foo },
  { path: '/bar', name: 'bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
  routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。通过 router 配置参数注入路
const app = new Vue({
  router
}).$mount('#app')

动态路由匹配

两种方式传递$route.params$route.query

模式

匹配路径

获取参数(路由信息对象)

/user/:username

/user/ligang

$route.params.username

/user?:username

/user?username=ligang

$route.query.username

响应路由参数的变化

​ 当使用路由参数时,例如从 /user/ligang 导航到 user/lg原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

方式一:简单地watch(监测变化)$route对象

 watch: {
    '$route' (to, from) {
      // 对路由变化作出响应...
    }
  }

方式二:使用 2.2 中引入的 beforeRouteUpdate 守卫

beforeRouteUpdate (to, from, next) {
  // 对路由变化作出响应...不要忘记调用next()
}

示例:新增和编辑使用同一模块,从编辑切换到新增页面信息不会更新!

{
  path: 'add',
  name: 'setting-user-manager-add',
  component: () => import('@/views/setting/user-manager/add-edit.vue'),
  meta: {name: '用户新增'}
}, {
  path: 'edit',
  name: 'setting-user-manager-edit',
  component: () => import('@/views/setting/user-manager/add-edit.vue'),
  meta: {
      name: '用户编辑',
      hidden: true
  }
}

嵌套路由

routes: [{ 
    path: '/user/:id', 
    component: User,
    children: [
        // 匹配 /user/:id
        { path: '', component: UserHome },
        // 匹配 /user/:id/profile
        { path: 'profile', component: UserProfile },
        // 匹配 /user/:id/posts
        { path: 'posts', component: UserPosts }
    ]
}]

要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

编程式导航

router.push(location, onComplete?, onAbort?)

声明式

编程式

<router-link :to="...">

router.push(...)

// 方式一:字符串路径
router.push('/user')
// 方式二:path对象
router.push({ path: '/user' })
// 方式三:路由名称
router.push({ name: 'user'})

注意:如果提供了 path,params 会被忽略,query不会!!

// 不生效
router.push({ path: '/user', params: { id: 1 }})
// params生效 /user/1
router.push({ name: 'user', params: { id: 1 }}) // 使用name方式
router.push({ path: `/user/1` }) // 直接在path上扩充
// query不受影响 /user?id=1
router.push({ path: '/user', query: { id: 1 }})

router.replace(location, onComplete?, onAbort?)

声明式

编程式

<router-link :to="..." replace>

router.replace(...)

router.push 很像,唯一的不同就是,它不会向 history 添加新记录!

router.go(n)

在 history 记录中向前或者后退多少步,类似 window.history.go(n)

命名视图

多个非嵌套视图展示,例如创建一个布局,有header头信息、 sidebar(侧导航) 和 main(主内容) 两个视图。

<router-view class="view header" name="header"></router-view>
<router-view class="view sidebar" name="sidebar"></router-view>
<router-view class="view main"></router-view>
const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: mainComponent,
        sidebar: sidebarComponent,
        header: headerComponent
      }
    }
  ]
})

重定向和别名

重定向

// 方式一:字符串路径path
{ path: '/a', redirect: '/b' }
// 方式二:name
{ path: '/a', redirect: {name: 'b'} }
// 方式三:动态返回重定向目标
{ path: '/a', redirect: to => {
  /* 方法接收 目标路由 作为参数;return 重定向的 字符串路径/路径对象 */
}}

别名

/a的别名是/b,意味着当用户访问/b时,URL会保持为/b,但是路由匹配则为/a,就像用户访问/a一样。

{ path: '/a', component: A, alias: '/b' }

『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

示例:上述【动态路由匹配】可修改成如下,可能存在name问题

{
  path: 'add',
  name: 'setting-user-manager-add',
  component: () => import('@/views/setting/user-manager/add-edit.vue'),
  meta: {name: '用户新增'},
  alias: 'edit'
}

向路由组件传递 props

路由组件传参

默认(常规)方式:通过$route.params获取

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
  routes: [{ path: '/user/:id', component: User }]
})

使用props解耦:只需要将props设置为true

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [{ path: '/user/:id', component: User, props: true }]
})

注意:上述props不仅可以设置为布尔值,还可以设置为对象或函数,具体请查看:「https://router.vuejs.org/zh-cn/essentials/passing-props.html

HTML5 History 模式

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

需要后台配置,否则输入的除首页外都为404(当然系统内跳转可以)。具体ngix、Apache、node等配置参考:「https://router.vuejs.org/zh-cn/essentials/history-mode.html

这里说一下本地webpack需要增加的配置情况:historyApiFallback: truehttps://doc.webpack-china.org/configuration/dev-server/#devserver-historyapifallback」vue-cli生成的默认webpack配置,

historyApiFallback: {
  rewrites: [
    { from: /.*/, to: path.join(config.dev.assetsPublicPath, 'index.html') },
  ],
}

在window下特定node版本会有问题!

导航守卫

『导航』表示路由正在发生改变

导航守卫主要用来通过跳转或取消的方式守卫导航。注意参数或查询的改变并不会触发进入/离开的导航守卫。可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

全局守卫

使用 router.beforeEach 注册一个全局前置守卫

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})

守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。所以确保要调用 next 方法,否则钩子就不会被 resolved

全局解析守卫

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

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

路由独享的守卫

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

组件内的守卫

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave

需要注意的是beforeRouteEnter不能访问this,可以通过传一个回调给 next来访问组件实例。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件内调用离开守卫beforeRouteLeave
  3. 调用全局beforeEach 守卫。
  4. 在重用的组件内调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 路由配置里调用独享守卫 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件内调用 beforeRouteEnter
  8. 调用全局beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

路由元信息

meta 字段来设置名称、是否需要验证、是否隐藏等附加信息!!

一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航守卫中的路有对象)的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。

if (to.meta.requireAuth) {   // 判断该路由是否需要登录权限
    if (store.state.token) {  // 通过vuex state获取当前的token是否存在
        next();
    }else {
        next({
            path: '/login',
            query: {redirect: to.fullPath}  // 将跳转的路由path作为参数,登录成功后跳转到该路由
        })
    }
}else {
    next();
}

数据获取

有时候,进入某个路由后,需要从服务器获取数据。

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。 该方式会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。 该方式在导航转入新的路由前获取数据。我们可以在接下来的组件内beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

滚动行为

只在 HTML5 history 模式下可用。当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

参考地址:「https://router.vuejs.org/zh-cn/advanced/scroll-behavior.html

特别说明

Router 实例

属性

说明

router.app

router 的 Vue 根实例

router.mode

路由使用的模式

router.currentRoute

当前路由对应的路由信息对象

方法:router.beforeEach(guard)、router.beforeResolve(guard) 、router.afterEach(hook)、router.push(location, onComplete?, onAbort?)、router.replace(location, onComplete?, onAbort?)、router.go(n)、router.back()、router.forward()、router.getMatchedComponents(location?)、router.resolve(location, current?, append?)、router.addRoutes(routes)、router.onReady(callback, [errorCallback])、router.onError(callback)

路由信息对象

每次成功的导航后都会产生一个新的对象

  • 在组件内,即 this.$route
  • $route 观察者回调内
  • router.match(location) 的返回值
  • 导航守卫的参数: router.beforeEach((to, from, next) => { // to 和 from 都是 路由信息对象 })
  • scrollBehavior 方法的参数: const router = new VueRouter({ scrollBehavior (to, from, savedPosition) { // to 和 from 都是 路由信息对象 } }) 其包含的属性值:route.path、route.path、route.params、route.query、route.query、route.hash、route.fullPath、route.fullPath、route.matched

重点强调:this.router.currentRoute===this.router.currentRoute === this.route

行为表现

因为它也是个组件,所以可以配合 <transition><keep-alive> 使用。如果两个结合一起用,要确保在内层使用 <keep-alive>

<transition>
  <keep-alive>
    <router-view></router-view>
  </keep-alive>
</transition>

router-link

<router-link> 比起写死的 <a href="..."> 会好一些,理由如下:

  • 无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
  • 在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
  • 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写(基路径)了。 base相关说明:「https://router.vuejs.org/zh-cn/api/options.html#base

实例

header编写

第一步:获取router的全部配置信息import {ROUTES} from '@/app.router',然后循环铺值(获取一级的路由)meta.name

第二步:选择header,路由跳转;主要思路:在一级组件上配置meta.defaultRouteName信息,获取该信息后,进行调整(如果不含有该信息,则默认第一个子路由)

第三步:处理当前选中的的header项目

 watch: {
  '$route': {
    // 必须,解决路由同步加载组件时,$watch首次不执行的问题
    immediate: true,
    handler (to) {
      if (to.matched[0]) {
        this.currentRoute = to.matched[0].name
      }
    }
  }
}

sidebar编写

基本思路和header相似,唯一区别是要根据当前一级路由进行铺值!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年01月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 简介
  • 动态路由匹配
    • 响应路由参数的变化
    • 嵌套路由
    • 编程式导航
      • router.push(location, onComplete?, onAbort?)
        • router.replace(location, onComplete?, onAbort?)
          • router.go(n)
          • 命名视图
          • 重定向和别名
            • 重定向
              • 别名
              • 向路由组件传递 props
                • 路由组件传参
                • HTML5 History 模式
                • 导航守卫
                  • 全局守卫
                    • 全局解析守卫
                      • 全局后置钩子
                        • 路由独享的守卫
                          • 组件内的守卫
                            • 完整的导航解析流程
                            • 路由元信息
                            • 数据获取
                            • 滚动行为
                            • 特别说明
                              • Router 实例
                                • 路由信息对象
                                  • 行为表现
                                    • router-link
                                    • 实例
                                      • header编写
                                        • sidebar编写
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档