Vue Router快速上手

路由(重要)

什么是路由?

  1. 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
  2. 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
  3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

基础

同一个路径可以匹配多个路由,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。

起步

在 vue 中使用 vue-router(非使用模块化机制编程方式)

  1. 导入 vue-router 组件类库:
<!-- 1. 导入 vue-router 组件类库 -->
<script src="./lib/vue-router-2.7.0.js"></script>
  1. 使用router-link 组件来导航 。
<!-- 2. 使用 router-link 组件来导航 -->
<!-- router-link默认渲染为一个a标签,可以使用tag属性改标签 -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
  1. 使用**router-view 组件来显示匹配到的组件**
<!-- 3. 使用 router-view 组件来显示匹配到的组件 -->
<!-- 专门用来当占位符,将路由规则匹配到的组件,就会展示到这个router-view中 -->
<router-view></router-view>
  1. 创建组件。
var login = {
    template: '<h1>登录组件</h1>'
}
var register = {
    template: '<h1>注册组件</h1>'
}
  1. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则
    • 导入vue-router包之后,在全局对象中,就有一个路由的构造函数VueRouter。
    • 每个路由规则都是一个对象,这个规则对象,身上有两个必须的属性;
      • 属性1是path,表示监听哪个路由链接地址;
      • 属性2是component,表示如果路由是前面匹配到的path,则展示component属性对应的那个组件。
    • 注意:component的属性值,必须是一个组件的模板对象,不可用是名称
    • redirect 路由重定向
var routerObj = new VueRouter({
   // routes表示路由匹配规则
    routes: [
        {path: '/', redirect: '/login'},
        {path: '/login', component: login},
        {path: '/register', component: register}
    ],
    linkActiveClass: 'myactive'
});
  1. 使用 router 属性来使用路由规则
    • router: 路由规则对象
var vm = new Vue({
    el: '#app',
    data: {},
    methods: {},
    // 将路由规则对象,注册到vm实例上,用来监听URL地址的变化
    router: routerObj
});

动态路由匹配

有时候需要使用同一组件,但渲染不同信息,比如信息模块,根据用户id来渲染不同的个人信息,但主体一样。这时候可以使用 动态路径参数 来完成。
一个路由中动态路径参数可无可多,如:/user/:username/post/:post_id

  • 动态路径参数 以冒号 : 开头。
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})
  • 当匹配到一个路由时,参数值会被设置到 this.$route.params ,可以在组件内使用。用法:this.$route.params.动态路径参数名
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}

注意:当使用动态路径参数的时候,如果只是动态路径参数改变的话,原来的组件会被复用,而不是重建,这意味着组件的生命周期函数不会再次被调用。

捕获所有路由或404 Not found 路由

* 匹配任何路由,通常用于客户端404错误,且一般含有通配符的路由都应该放在最后。

{
  // 会匹配所有路径
  path: '*'
}

当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:

// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'

嵌套路由children

所谓嵌套路由就是,当一个路由匹配到一个组件的时候,该组件下又含有需要路由匹配的组件,简单来说就是父组件嵌套子组件,但这个子组件根据路由来渲染的。

前面所说,<router-view> 是用于放置路由匹配到的组件,并且是渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>

注意:利用children来实现子路由,子路由的path不用加/,否则永远以根路径开始请求。大致配置方法与routes配置一样

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
        }
      ]
    }
  ]
})

编程式的导航

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

在 Vue 实例内部,可以通过 $router 访问路由实例。因此想要导航到不同的 URL,则使用 router.push 方法。实际使用 <router-link> 时内部就是调用这个方法。

声明式编程式
<router-link :to="...">router.push(...)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

注意:如果提供了 path,params 会被忽略,例子中的 query 并不属于这种情况。

const userId = '123'
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

附:
可选的在 router.pushrouter.replace 中提供 onCompleteonAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。

如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)。

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

router.push 差不多,唯一的不同就是,它不会向history添加新记录,而是替换掉当前的history记录。

声明式编程式
<router-link :to="..." replace>router.replace(...)

router.go(n)

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步。

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)

命名路由

命名路由就是在创建Router实例的时候,在routes配置中给某个路由设置名称。

const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: User
    }
  ]
})
  • 使用router-link链接一个命名路由:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

命名视图

在同时(同级)展示多个视图,而不是嵌套视图,就可以使用命名视图,router-view通过一个name属性,与路由规则中的 components 里的属性名对应,如果router-view没有设置名字,则默认是default
在嵌套路由中也可以使用。

注意:一个视图使用一个组件渲染,如果同个路由,多个视图就需要多个组件,则确保comonents配置(带上s)

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="lib/vue.js"></script>
    <script src="lib/vue-router.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .header {
            background-color: yellow;
            height: 100px;
        }
        .container {
            display: flex;
            height: 300px;
        }
        .left {
            background-color: green;
            flex: 2;
        }
        .main {
            background-color: pink;
            flex: 8;
        }
    </style>
</head>
<body>
<div id="app">
    <router-view></router-view>
    <!-- name属性对应router中components中的属性名 -->
    <div class="container">
        <router-view name="left"></router-view>
        <router-view name="main"></router-view>
    </div>
</div>
<script>
    var header = {
        template: '<h1 class="header">Header头部区域</h1>'
    }
    var leftBox = {
        template: '<h1 class="left">Left侧边栏区域</h1>'
    }
    var mainBox = {
        template: '<h1 class="main">mainBox主体区域</h1>'
    }
    var router = new VueRouter({
        routes: [
            {
                path: '/', components: {
                    'default': header,
                    'left': leftBox,
                    'main': mainBox
                }
            }
        ]
    });
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router
    });
</script>
</body>
</html>

重定向和别名

重定向redirect

重定向通过routes配置redirect属性来完成

// 从/a 重定向到 /b
const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
    { path: '/c', redirect: { name: 'foo' }}
    { path: '/d', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

别名alias

向通过routes配置alias属性来完成

// 当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,URL还是 /b
const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

路由组件传参

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

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

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

布尔模式(如上例子)

如果 props 被设置为 trueroute.params 将会被设置为组件属性。

对象模式

如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。

const router = new VueRouter({
  routes: [
    { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
  ]
})

函数模式

创建一个函数返回 props。这样可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

// URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。
const router = new VueRouter({
  routes: [
    { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
  ]
})

History模式

vue-router默认 hash 模式,但可以通过router实例配置 mode 属性来切换为history模式。

注意:history模式需要后台配置支持

导航守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
记住参数或查询的改变并不会触发进入/离开的导航守卫。可以通过$route对象来观察。

全局前置守卫

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中
使用 router.beforeEach 注册一个全局前置守卫:

确保要调用 next 方法,否则钩子就不会被 resolved。

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 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-linkto proprouter.push 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

全局解析守卫

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

全局后置钩子

router.afterEach((to, from)注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

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

路由独享的守卫

在路由配置上直接定义 beforeEnter 守卫(参数与全局前置守卫一样):

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

组件内的守卫

在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

注意:beforeRouteEnter 守卫 不能 访问 this,但可以通过传一个回调给 next 来访问组件实例,在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。beforeRouteEnter 是支持给 next 传递回调的唯一守卫。

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

beforeRouteLeave 通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from , next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

完整的导航解析流程

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

路由元信息meta

routes 配置中的每个路由对象为 路由记录
一个路由匹配到的所有路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。因此,可以遍历 $route.matched 来检查路由记录中的 meta 字段。
定义路由的时候可以配置 meta 字段

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

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()
  }
})

过渡效果(省略)

与vue的动画效果用法一样

vue进阶笔记

数据获取

有时候,进入某个路由后,需要向服务器获取数据用于渲染。可以在以下两种方式来实现:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据(通常为created)。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
<template>
  <div class="post">
    <div class="loading" v-if="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>

导航完成后获取数据

export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // 组件创建完后获取数据,
    // 此时 data 已经被 observed 了
    this.fetchData()
  },
  watch: {
    // 如果路由有变化,会再次执行该方法
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}

在导航完成前获取数据

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
      }
    }
  }
}

滚动行为

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

在这里插入图片描述
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。可以在 router 实例内提供一个scrollBehavior 方法

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
    return { x: 0, y: 0 }
	// 模仿浏览器原生表现
	// if (savedPosition) {
    //     return savedPosition
    // } else {
    //     return { x: 0, y: 0 }
    // }
  }
})

scrollBehavior 方法接收 tofrom 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。返回滚动位置的对象信息:

  • { x: number, y: number }
  • { selector: string, offset? : { x: number, y: number }}

路由懒加载

使用Vue 的 异步组件 和 Webpack的 代码分割功能 来实现组件的懒加载。

https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#把组件按组分块

路由对象

  • $route.path
    • 类型: string
      字符串,对应当前路由的路径,总是解析为绝对路径,如 “/foo/bar”。
  • $route.params
    • 类型: Object
      一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
  • $route.query
    • 类型: Object
      一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。
  • $route.hash
    • 类型: string
      当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
  • $route.fullPath
    • 类型: string
      完成解析后的 URL,包含查询参数和 hash 的完整路径。
  • $route.matched
    • 类型: Array<RouteRecord>
      一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。
  • $route.name
    当前路由的名称,如果有的话。(查看命名路由)
  • $route.redirectedFrom
    如果存在重定向,即为重定向来源的路由的名字
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值