vue-router基本使用

Vue Router功能

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

安装

  1. CDN
     <script src="https://unpkg.com/vue/dist/vue.js"></script>
     <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    
  2. npm
    npm install --save-dev vue-router
    
    import Vue from 'vue';
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    

基本用法

在html中应用

<div id="app">
    <h1>Hello App!</h1>
    <p>
        <!-- 使用 router-link 组件来导航. -->
        <router-link to="/foo">Go to Foo</router-link>
        <router-link to="/bar">Go to Bar</router-link>
    </p>
    <!-- 路由出口 路由匹配到的组件将渲染在这里-->
    <router-view></router-view>
</div>

js部分

// 1. 定义组件
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由。每个路由应该映射一个组件
const router = new VueRouter({
    routes:[
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]
})
// 3. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
const app = new Vue({
    router
}).$mount('#app')

模块化应用

  1. vue create一个实例
  2. 创建组件(components文件夹)
  3. router.js
//1.加载vue与vue-router
//2.导入相应组件
//3.编写并导出路由

import VueRouter from 'vue-router'
Vue.use(VueRouter)
import Vue from 'vue'
import PageHome from './components/pages/Home';
import PageAbout from './components/pages/About';
const router=new VueRouter({
    routes:[
        {
            path:'/',
            component:PageHome
        },
        {
            path:'/about',
            component:PageAbout
        },
    ]
})
export default router
  1. main.js导入路由
  2. App.vue呈现路由视图<router-view/>

嵌套路由

嵌套路由允许指定子路由,并且用另一个<router-view/>来显示其内容。假设这里有两个单页面,一个为address一个为email,他们共用header和nav:

image.png

  1. 在common中创建两个共用组件:NavHeaderNavLeft

  2. 在pages创建主页面Settings.vue和两个子组件SettingsAddressSettingsEmail

    image.png

  3. 将公共组件放在主页面Settings.vue中,其中router-view展现的是其子路由。可以适当加样式

    <template>
       <div>
          <NavHeader/>
          <NavLeft/>
          <router-view/>
       </div>
    </template>
    
    <script>
    import NavHeader from '../common/NavHeader'
    import NavLeft from '../common/NavLeft'
    export default{
       name:'Settings',
       components:{
          NavHeader,
          NavLeft
       }
    }
    </script>
    
  4. router.js

    //...
    import PageSettings from './components/pages/Settings';
    import PageSettingsEmail from './components/pages/SettingsEmail';
    import PageSettingsAddress from './components/pages/SettingsAddress';
    
    const router=new VueRouter({
       routes:[
          {
                path:'/settings',
                component:PageSettings,
                children:[
                   {
                      path:'email',
                      component:PageSettingsEmail
                   },
                   {
                      path:'address',
                      component:PageSettingsAddress
                   },
                ]
          },   
       ]
    })
    export default router
    

    最后呈现:
    image.png

重定向和别名

有些时候,你不希望前往/a访问的用户看到一个错误页面,也不希望搜索引擎链接到一堆不存在的页面上。那么可以用上redirect,这个时候任何对/a的访问都会被重定向到/b

const router=new VueRouter({
   routes:[
      {
            path:'/a',
            redirect:'/b',
      },   
   ]
})

那么“别名”又是什么呢?

如下面的例子。/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由其实指向 /a,就像用户访问 /a 一样。

const router = new VueRouter({
  routes: [
    { 
        path: '/a', 
        alias: '/b' ,
        component: A, 
    }
  ]
})

命名路由

//有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接跳转路由的时候,下面的编程式导航会有例子
const router = new VueRouter({
  routes: [
    {
      path: '/user,
      name: 'user',//命名路由
      component: User
    }
  ]
})

命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示。例如一个布局有 sidebarmain两个视图,而你不想嵌套,那么你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>

一个视图使用一个组件渲染。因此对于同个路由,多个视图就需要多个组件。注意此时要写成components

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

HTML5 History模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。我们可以让vue-router使用这种HTML5 history API,只需要改变路由的模式

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

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id。不过还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 不存在的url 就会返回 404,这就不好看了。所以需要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

动态路由

基础应用

动态路由的使用场景很多。比如你想匹配用户的ID作为路径的一部分,就可以用到动态路由,动态路由用 :标记:

const router = new VueRouter({
  routes: [
    { 
        path: '/user/:id',
    	component: PageUser 
    }
  ]
})

路由参数

在组件实例中,可以通过使用属性this.$route来获取当前的路由对象。其中 this.$route.params可以访问到动态路由参数。

//访问:`user/1234`:
{
    "id":'1234'
}
//如果有多个动态参数如/user/:Id/posts/:pageNumber
//访问:/user/1234/posts/2 
{
    "id":"1234",
    "pageNumber":"2"
}

响应路由变化

当从 /user/foo 和 /user/bar相互切换时,原来的组件实例会被复用。意味着组件的生命周期钩子不会再被调用。如果想对路由参数的变化作出响应的话:

  • 使用 watch 监测$route 对象:

    const PageUser = {
      template: '...',
      watch: {
          $route(to, from) {
          // 对路由变化作出响应...
          }
      }
    }
    
  • 使用beforeRouteUpdate导航守卫

    const PageUser = {
      template: '...',
      beforeRouteUpdate (to, from, next) {
          // 对路由变化作出响应...
          next()
      }
    }
    

路由传参

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

原本:

const User = {
  template: '<p>user:{{this.$route.params.id}}</p>',
}

const router = new VueRouter({
  routes: [
    { 
        path: '/user/:id',
    	component: User 
    }
  ]
})

现在:

const User = {
  props:['id']
  template: '<p>user:{{id}}</p>',
}

//在路由中指定props为true
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 }
    }
  ]
})

导航

锚点导航

<!--一个动态跳转路由的例子-->
<li v-for="(item,index) in phoneList" :key="index">
      <a target="_blank" :href="'/#/product/'+item.id"></a>
</li>

链接导航

在起步中已经用到过:<router-link>

  • 通过传入 to 属性指定链接

  • 默认会被渲染成一个 <a> 标签

  • 通过tag属性改变默认标签

    <router-link to="/foo" tag="li">Go to Foo</router-link>
    
  • 优化:可以在<router-link>里面加上锚点标签

    <router-link to="/foo"><a>Go to Foo</a></router-link>
    //这样就可以使用原生浏览器行为
    
  • 原生事件

    <router-link to="/foo" @click.native="handleclick">Go to Foo</router-link>
    

编程式导航

我们还可以借助 router 的实例方法实现路由跳转:

  • router.push()

    router.push({ path: 'home' })// ===> /home
    const userId = '123'
    router.push({ path: `/user/${userId}` }) // ===> /user/123
    
    //当路由有名字时
    router.push({ name: 'user'}) // ===> /user
    

    这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

  • router.replace()

    不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

  • router.go(n)

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

导航守卫

导航守卫主要用来通过跳转或取消的方式来控制导航操作

全局前置守卫

  • router.beforeEach()

假设你想要限制未登录的用户访问你应用的某些部分,同时有一个userAuthenticated()方法用于当用户登录时返回true。这个时候就可以用到导航守卫

该守卫有3个参数:tofromnext。其中from和to分别表示导航从哪里来和到哪里去,next则是一个回调,在里面你可以让vue-router去处理导航、取消导航、重定向到其他地方或者注册一个错误。

const router = new VueRouter({ ... })
router.beforeEach((to,from,next)=>{
	if(to.path.startsWidth('/account')&&!userAuthenticated()) next('/login');
	else next()
})
//解析:如果被导向的路由的路径以/account开头,而用户还未登录,则该用户会被重定向到/login;
//否则,就调用next(),用户就能看到他们所请求的account页面

全局后置钩子

  • router.afterEach()

这个方法运行在导航之后。只被传入两个参数,to和from,因此不会影响导航

const router = new VueRouter({
    routes: [
        {
            path: '/blog',
            component:Blog,
            meta:{
                title:'welcome'
            }
        }
    ]
})

router.afterEach((to)=>{
	document.title=to.meta.title
})

路由独享守卫

  • beforeEnter
const router = new VueRouter({
    routes: [
        {
            path: '/blog',
            component:Blog,
            beforeEnter(to,from,next){
                //...
            }
        }
    ]
})

组件内部守卫

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

  • beforeRouteEnter等效于(beforeEach)
  • beforeRouteUpdate
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 组件实例还没被创建,不能获取组件实例 `this`
     next(vm=>{..})//可用过vm访问组件实例
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    //这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
    const answer = window.confirm('Do you really want to leave? ')
      if (answer) {
        next()
      } else {
        next(false)
      }
  }
}

完整的导航解析流程

  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 meta fields)就发挥作用了。现在你只需在需要检查的路由上添加一个meta,里面写一个requiresAuth:true,这时守卫就能在那里获取它了。

const router = new VueRouter({
    routes: [
        {
            path: '/account',
            component:Account,
            meta:{
                requiresAuth:true
            }
        }
    ]
})

router.beforeEach((to,from,next)=>{
    if(to.meta.requiresAuth&&!userAuthenticated()){
        next('/login');
    }else{
        next()
    }
})

当使用嵌套路由时,to.meta指向的是子路由的元信息,而非其父路由。这个时候可以通过遍历to.matched,它会包含父路由的元信息:

router.beforeEach((to,from,next)=>{
    const requiresAuth=to.matched.some((record)=>{
        return record.meta.requiresAuth
    })
    if(requiresAuth&&!userAuthenticated()){
        next('/login');
    }else{
        next()
    }
})

404页面

路由顺序:vue-router在内部通过遍历路由数组的方式来挑选被显示的路由,并选取其中匹配到当前URL的第一个。这意味着安排好路由的顺序很重要。

现在,可以利用vue-router会按顺序搜索路由直到与通配符(*)匹配的特点,来渲染一个显示错误页面:

const router = new VueRouter({
    routes: [
        //...你的其他路由
        {
            path: '*',
            component:NotFound,//其他路由都匹配不到时,就会显示NotFound组件
        }
    ]
})

在使用嵌套路由时,如果没有匹配到子路由,则路由器会继续往下对其父路由之外的路由列表进行搜寻。如果想让子路由的错误页面也能在父组件中显示,则需要在子路由数组中添加该通配符路由:

const router = new VueRouter({
    routes: [
        {
            path:'settings',
            component:Settings,
            children:[
                {
                    path:'address',
                    component:Address,
                },
                {
                    path: '*',
                    component:NotFound,//在子路由中添加
                }
            ]
        }
        {
            path: '*',
            component:NotFound,
        }
    ]
})
posted @ 2020-09-01 15:57  sanhuamao  阅读(138)  评论(0编辑  收藏  举报