vue-router,你全部都清楚了吗?

429 阅读5分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

说在前面

  1. vue单页应用通过路由(vue-router)实现页面跳转;所以单页应用才需要路由
  2. 一个页面跳转是一个路由(route),他是一组映射关系(key - value)
    1. key是路径,value是组件
  3. 多个路由需要路由器(router)进行管理
  4. vue-router在vue中使用时是在vue实例添加一个配置项叫router
  5. router和vue实例一样需要new,所以VueRouter的首字母大写,new时需要传入一个对象作为配置项,对象含一个重要属性routers,属性值为数组;也可以传入其他属性如mode
  6. vue-router是vue的一个插件,使用三步曲
    1. 安装npm i vue-router
    2. 引入import VueRouter from 'vue-router'
    3. 使用Vue.use(VueRouter)

路由中几个重要的点

  1. 路由的切换
    1. 可以借助 router-link 标签实现
    2. 也可以使用 push、 replace、 forward、 back、 go 等js方法实现
  2. 需要指定组件呈现的位置 使用<router-view></router-view>
  3. 路由组件通常放到pages文件夹下面,一般组件放到components文件夹

image.png

  1. 通过切换,隐藏了的路由组件,默认是被销毁了的,需要的时候会再挂载
  2. 每个组件都有自己的$route属性,里面存储着自己的路由信息
  3. 整个应用只有一个router,可以通过组件的$router属性获取到

实现一个简单的路由切换

  • 路由中的组件可以先引入后使用,也可以按需引入
  • 引入路由时文件的.vue后缀可以省略;引入js文件时,如果引入某个文件夹下的index.js,那么index.js可以省略;建议不要省略
  • 配置路由的时候path对应的属性值必须加上'/'

公用代码如下:

main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'


//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)


//创建vm
new Vue({
    el:'#app',
    render: h => h(App),
    router:router
})

router/index.js

// 引入路由
import VueRouter from 'vue-router'
//引入组件(先定义后使用)
import Home from '../pages/home.vue'


//创建并暴露一个路由器
export default new VueRouter({
    routes:[
        {
            path:'/about',
            // 也可以按需引入
            component: () => import('@/pages/about.vue')
        },
        {
            path:'/home',
            component:Home
        }
    ]
})

pages/home.vue

<template>
    <h2>我是home的内容</h2>
</template>


<script>
    export default {
        name:'Home'
    }
</script>

pages/about.vue

<template>
    <h2>我是about的内容</h2>
</template>


<script>
    export default {
        name:'About'
    }
</script>

使用router-link标签实现

  • 使用的时候to的值可以不加前面的'/',为了兼容性和统一性建议加上
  • router-link标签
    • 默认被解析为a标签;通过tag属性修改
    • 默认激活(非精准匹配)时添加类名router-link-active;通过active-class属性修改
    • 默认激活(精准匹配)时添加类名router-link-exact-active;通过exact-active-class属性修改
    • 精准匹配是值to的值完全和路由相等,非精准匹配是指路由被包含就会触发;特别注意to='/'在非精准匹配的情况下会一直被触发active;如果要完全按照精准匹配来可以设置
<router-link
    to="/about"
    active-class="active">About</router-link><br>
<router-link to="/home">Home</router-link><br>
<router-link to="/about/list"
    exact-active-class="active1">/about/list</router-link><br>
<!-- 用于展示路由组件 -->
<router-view></router-view>

image.png

image.png

使用push方法标签实现

<template>
    <div id="app">
        <hello msg="Hello"/>
        <li @click="goToRouter('/about')">About</li>
        <li @click="goToRouter('/home')">Home</li>
        <!-- 用于展示路由组件 -->
        <router-view></router-view>
    </div>
</template>


<script>
import hello from './components/hello.vue'


export default {
    name: 'App',
    components: {
        hello
    },
    methods: {
        goToRouter(path){
            this.$router.push({path});
        }
    }
}
</script>

image.png

多级路由

  • 为需要子路由的路由对象添加children属性,属性值是一个数组可以放多个子路由,每个子路由的配置项和父级路由完全一样
  • 特别注意,是哪个组件(页面)的子路由,那么<router-view></router-view>就应该写在那个组件(页面)里面
  • 子路由的path属性值如果前面加/,跳转时路径应该配置为path属性对应的完整值;如果子路由的path属性前面不加/,跳转时路径配置为父级路由的path属性值+/+子路由的path属性值
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import Home from '../pages/home'
import About from '../pages/about'
//创建并暴露一个路由器
export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            children:[{
                path:'/about/list',
                component:() => import('../pages/list.vue')
            }]
        }
    ]
})

命名路由和动态路由

命名路由:

  • 在配置路由的时候添加一个name属性;
  • 可以直接使用name属性跳转,不必使用path
  • 使用命名路由,使用params参数时可以自动解码和编码

动态路由:

  • 使用:的格式占位,后面跟着的名称对应params中的属性名
  • 使用path跳转,将属性值拼接在路径后面即可,页面跳转后刷新页面params不会消失,在跳转之后的页面中使用$route.params接收参数;多拼接参数将不能跳转(路径不对)
  • 使用name跳转时,只需配置params对象属性,对象属性中可以添加任意的参数,如果参数名和动态路由中的属性名一致,那么页面刷新params参数还在,否则页面刷新params参数消失

router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import Home from '../pages/home'
import About from '../pages/about'
//创建并暴露一个路由器
export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            children:[{
                // 这里就是动态路由
                path:'/list/:id/:title',
                name:'list',
                component:() => import('../pages/list.vue')
            }]
        }
    ]
})

home.vue

<template>
    <div>
        <h2>我是home的内容</h2>
        <button @click="goTo">跳转</button>
        <!-- <router-link to="/list/123/456">跳转</router-link> -->
        <router-view></router-view>
    </div>
</template>


<script>
    export default {
        name:'Home',
        methods: {
            goTo(){
                this.$router.push({
                    name:'list',
                    params:{
                        id:'123',
                        title:'456',
                        nr:'789'
                    }
                })
            }
        }
    }
</script>

list/index.js

<template>
    <h2>我是list的内容{{$route.params}}</h2>
</template>


<script>
    export default {
        name:'List'
    }
</script>

image.png

image.png

image.png

路由参数

params参数

  • 使用了占位符(动态路由)的params参数,会跟在路径后面,刷新不会消失
  • 其实不只是命名路由可以传递params参数,只是使用path传递params参数需要提前定义成动态路由,留出params参数的位置
  • 更多内容看上面一节
  • 获取参数使用$route.params

query参数

  • 类似http的get请求的参数,拼接到请求路径后面
  • 使用方法:可以直接拼接在path路径上;也可以使用query属性
  • 以下几种方法都是可以的
<template>
    <div>
        <h2>我是about的内容</h2>
        <router-link to="/news?a=123&b=456">
          点击跳转news
        </router-link>
        <br>
        <router-link :to="{path:'/news',query:{a:1}}">
          点击跳转news
        </router-link>
        <br>
        <router-link :to="{name:'news',query:{a:1,b:1}}">
          点击跳转news
        </router-link>
        <br>
        <button @click="goTo">点击跳转news</button>
        <router-view></router-view>
    </div>
</template>


<script>
    export default {
        name:'About',
        methods: {
            goTo(){
                this.$router.push({
                    name:'news',
                    query:{
                        id:'123',
                        title:'456',
                        nr:'789'
                    }
                })
            }
        }
    }
</script>

image.png

所以想要刷新参数不消失,要么直接传query参数,要么传params参数的时候,在路由中使用动态路由占位(命名路由使用params传参数是不需要占位的,但是不占位刷新页面参数会丢失)

路由跳转的其他方式

  • 除了push还可以使用replace
  • 不同的是push是向浏览器历史记录中添加一条记录;replace是替换浏览器中的当前记录
  • router-link默认开启的是push跳转方式;添加replace改变跳转方式
<router-link replace to="/news">news</router-link>
  • 作用是使用replace跳转的路由,点击浏览器的返回按钮,页面将跳转到之前不是使用replace跳转的页面
  • $router.forward()前进
    • 前进只针对于有浏览器历史记录的路由
  • $router.back()后退,同前进
    • 如果上一个路由使用replace方式跳转,则后退不到上一个路由
  • $router.go()可前进也可后退

props配置

  • 可以在路由的配置项中添加props配置项;在路由组件中以props的形式接收
  • 配置方式有三种
  1. 直接是个对象,对象里面的属性将在跳转的路由组件中以props的形式被接收

image.png

  1. 可以是布尔值,如果为true,则把路由收到的所有params参数可以在路由组件中以props的形式接收

image.png

  1. 上面那种形式props只能接收params传的值;可以函数的形式传递route中的任意参数,如query参数

image.png

image.png

keep-alive组件标签

  • Vue 内置的一个组件,可以使被包含的组件保留状态,让不展示的组件保持挂载,不被销毁
  • 可以缓存任意组件(包含一般组件和路由组件)
// 缓存一般组件
<keep-alive>
    <hello @click="change(arguments,2)" msg="Hello"/>
</keep-alive>
// 缓存路由组件
<keep-alive exclude="About">
    <router-view></router-view>
</keep-alive>

image.png

  • 可以在标签添加两个属性include和exclude,值可以是字符串或者正则表达式
  1. include-只有匹配组件名的组件会被缓存
// 只有About路由组件可以被缓存
<keep-alive include="About">
    <router-view></router-view>
</keep-alive>
  1. exclude-任何匹配组件名的组件都不会被缓存
// 只有About路由组件不可以被缓存
<keep-alive exclude="About">
    <router-view></router-view>
</keep-alive>
  • 被缓存的组件新增两个生命周期钩子
    • activated();组件每次被激活时触发
    • deactivated();离开被缓存的组件时激活

image.png

上图可以看到:

  1. 只有第一次进入缓存组件时才会触发createdmounted相关钩子函数,后面进入缓存组件不会触发
  2. 离开不会触发destroyed相关钩子
  3. 每次进入会触发activated钩子
  4. 每次离开会触发deactivated钩子
  5. 因为组件没有销毁所以离开再次进入都会触发updated相关钩子
  • 只想缓存某些路由组件;除了include和exclude,还可以配置路由实现
    • 配置路由的meta属性;meta称为路由元信息,你可以将任意信息附加到路由上;如权限点等,这些信息都可以配置到meta对象属性中
    • 下面例子中message路由组件将不会被缓存,news路由组件将被缓存
// router/index.js
export default new VueRouter({
    routes:[
        {
            path:'/about',
            name:'about',
            component:About,
            children:[{
                path:'/news',
                name:'news',
                component:() => import('../pages/news.vue'),
                meta: {
                    keepAlive:true
                }
            },{
                path:'/message',
                name:'message',
                component:() => import('../pages/message.vue'),
                meta: {
                    keepAlive:false
                }
            }]
        }
    ]
})
// about组件
<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

路由守卫

  • 作用:对路由进行权限控制;通过取消和跳转的方式来进行导航守卫
  • 分类:全局守卫、独享守卫、组件内守卫
  • 全局守卫又分为全局前置守卫和全局后置守卫
  1. 全局前置守卫(beforeEach): 初始化时执行一次,每次路由切换前执行;守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中

该守卫方法接收三个参数

to:即将进入的目标导航,包含该路由的全部信息

image.png

from:当前导航正要离开的路由;信息包含的内容项和to一样;如果是初始化进入时from的信息和to的信息完全一致 next:调用next方法,将跳转到管道中的下一个路由;也可以指定跳转路由

router.beforeEach((to,from,next)=>{
    if(!to.meta.isLogin && to.path !== '/home'){
        // 指定跳转路由
        next({path:'/home'});
    }else {
        next()
    }
})
  1. 全局后置守卫
  • 初始化的时候被调用一次,每次路由切换的时候被调用
  • 参数只有to和form,一般用于修改网页的title等
router.afterEach((to,from)=>{
    console.log('后置路由守卫',to,from)
    document.title = to.meta.title || '系统'
})
  1. 独享守卫
  • 写在需要守卫的路由配置中
  • 如果只针对某个路由进行守卫,可以使用独享守卫
  • 每次进入当前路由的时候执行,参数和全局前置守卫一样,能做的操作也是一样
  • 如果既有全局前置守卫也有独享守卫,先执行全局前置守卫再执行独享守卫

image.png

beforeEnter(to,from,next){
    console.log('独享路由守卫',to,from);
    next()
}
  1. 组件内守卫:分为进入守卫(beforeRouteEnter)和离开守卫(beforeRouteLeave) 写在组件内部,和生命周期钩子一样属于组件配置项的一个属性

进入守卫:

  • 通过路由规则进入该组件时被调用;在进入组件的beforeCreated之前会被调用,所以在此时还没有this
  • 在此时可以修改路由参数中的任意值;下面就是根据query中的参数修改meta的值
// 通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next) {
    if (to.query.jdmc) {
        const last = `${to.query.jdmc}阶段风险问题统计`;
        to.meta.breadcrumb = ['风险防控', '执行风险防控', '风险概览'];
        to.meta.breadcrumb.push(last);
    }
    next();
}

离开守卫;离开组件时被调用

beforeRouteLeave (to, from, next) {
    console.log('About--beforeRouteLeave',to,from)
    next()
}

路由工作的两种模式

  1. hash模式:
    1. 对于一个url来说,什么是hash值?
      1. #及其后面的内容就是hash值。
      2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
      3. 所以部署上线的时候不用返回每个地址,所以不需要特殊处理
    2. 地址中永远带着#号,不美观 。
    3. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    4. 兼容性较好。
  2. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
      1. 因为地址改变就会请求服务器,所以需要服务器能返回对应的地址