介绍

什么是路由?

  1. 一个路由就是一个映射关系(key:value)
  2. key为路径, value可能是function或component

路由分类
后端路由:

  1. 理解: value是function, 用来处理客户端提交的请求。
  2. 注册路由: router.get(path, function(req, res))
  3. 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

前端路由:

  1. 浏览器端路由,value是component,用于展示页面内容。
  2. 注册路由:
  3. 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

    面试题

  1. 路由传递参数(对象写法)path是否可以结合params参数一起使用?
    • 不可以程序会崩掉,必须使用name
  2. 如何指定params参数可传可不传?
    • 在配置路由时加上? path:"/search/:key?"
    • 如果指定name与params配置, 但params中数据是一个””, 无法跳转,路径会出问题
  3. params参数可以传递也可以不传递,但是如果传递是空串,如何解决?
    • 传空也会有问题, 可以使用undefined解决 this.router.push({name:'xx', params:{key : '' || undefined}})
  4. 路由组件能不能传递props数据?

    • 可以,有三种形式 参考 7.路由的props配置

      1.基本使用

      安装配置

  5. 安装vue-router,命令:npm i vue-router@3

  6. 应用插件:Vue.use(VueRouter)
  7. 编写router配置项:

    router/index.js

  1. //引入VueRouter
  2. import VueRouter from 'vue-router'
  3. //引入Luyou 组件
  4. import About from '../components/About'
  5. import Home from '../components/Home'
  6. //创建router实例对象,去管理一组一组的路由规则
  7. const router = new VueRouter({
  8. routes:[
  9. {
  10. path:'/about',
  11. component:About
  12. },
  13. {
  14. path:'/home',
  15. component:Home
  16. }
  17. ]
  18. })
  19. //暴露router
  20. export default router

main.js

  1. //引入VueRouter
  2. import VueRouter from 'vue-router'
  3. //引入路由器
  4. import router from './router'
  5. //注册路由
  6. Vue.use(VueRouter)
  7. //创建vm
  8. new Vue({
  9. el:'#app',
  10. render: h => h(App),
  11. router:router
  12. })

使用路由

  1. link view 可以不用在同组件中
  2. <!-- Vue中借助router-link标签实现路由的切换 -->
  3. <router-link to="/about">About</router-link>
  4. <!-- 指定组件的呈现位置 -->
  5. <router-view></router-view>

路由样式 -active-class

  1. active-class可配置高亮样式 (默认是router-link-active)
  2. linkActiveClass 修改active样式名 ```javascript About

//创建router实例对象时统一配置标签激活状态class const router = new VueRouter({ routes, //修改router-link标签激活状态class linkActiveClass: “dan-active” });

<a name="dgt2c"></a>
#### 按需加载
run build时会单独生成js和css文件,点击路由导航时才会在newwork加载
```javascript
//项目中这样写 ,记得一定要写()=>
const routes = [
 {
   path:'/singer',
   component:()=>import('../views/Singer'),
    }
]

//这种写法留着看大概原理
const Singer = (resolve)=>{
  import('../views/Singer').then((moduel)=>{
   resolve(moduel);
  })
}

const routes = [
 {
   path:'/singer',
   component:Singer,
    }
]

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(路由嵌套)

  1. 配置路由规则,使用children配置项:

path:’news’, //此处一定不要写:/news
children:[ ] 是数组不是对象

routes:[
    {
        path:'/about',
        component:About,
    },
    {
        path:'/home',
        component:Home,
        children:[ //通过children配置子级路由
            {
                path:'news', //此处一定不要写:/news
                component:News
            },
            {
                path:'message',//此处一定不要写:/message
                component:Message
            }
        ]
    }
]
  1. 跳转(要写完整路径):
    <router-link to="/home/news">News</router-link>
    

4.路由的query参数

  1. 传递参数
    ```vue 跳转

<router-link :to=”{ path:’/home/message/detail’, query:{ id:666, title:’你好’ } }”

跳转 ```

  1. 接收参数:
    ```vue


<a name="d12350da"></a>
### 5.命名路由

1.  作用:可以简化路由的跳转。 
1.  如何使用 
   1.  给路由命名:  
```javascript
{
    path:'/demo',
    component:Demo,
    children:[
        {
            path:'test',
            component:Test,
            children:[
                {
          name:'hello' //给路由命名
                    path:'welcome',
                    component:Hello,
                }
            ]
        }
    ]
}
  1. 简化跳转:
    ```vue 跳转

跳转

<router-link :to=”{ name:’hello’, query:{ id:666, title:’你好’ } }”

跳转 ```

6.路由的params参数

  1. 配置路由,声明接收params参数

    {
    path:'/home',
    component:Home,
    children:[
        {
            path:'news',
            component:News
        },
        {
            component:Message,
            children:[
                {
                    name:'xiangqing',
                    path:'detail/:id/:title', //使用占位符声明接收params参数
                    component:Detail
                }
            ]
        }
    ]
    }
    
  2. 传递参数
    ```vue

    跳转

<router-link :to=”{ name:’xiangqing’, params:{ id:666, title:’你好’ } }”

跳转 ``` 特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  1. 接收参数:
    $route.params.id
    $route.params.title
    

7.路由的props配置

作用:让路由组件更方便的收到参数 (this.$route.name => name)

router/index.js

{
    name:'xiangqing',
    path:'detail/:id',
    component:Detail,

    //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    // props:{a:900}

    //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
    // props:true

    //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
  //参数params query都可以
    props(route){
        return {
            id:route.query.id,
            title:route.query.title
        }
    }
}
//接收
props:['id','title']

//使用
<template>
    <ul>
        <li>消息编号:{{id}}</li>
        <li>消息标题:{{title}}</li>
    </ul>
</template>

8.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活
  2. 具体编码: $router.push/replace() ```javascript //$router的两个API this.$router.push({ //path:’xxx’,//也可以是path name:’xiangqing’,
    params:{
        id:xxx,
        title:xxx
    }
    
    })

this.$router.replace({ name:’xiangqing’, params:{ id:xxx, title:xxx } })

this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退- 接收参数number表示前进或后退几次

<a name="aCkgK"></a>
#### 重写 push/replace (V4不会不用重写)
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1624878/1639480776412-59b86b49-015e-4091-ab5b-3a9d0dcb2945.png#clientId=u6e13e53d-9a7a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=94&id=u744d2ba9&margin=%5Bobject%20Object%5D&name=image.png&originHeight=187&originWidth=1169&originalType=binary&ratio=1&rotation=0&showTitle=false&size=232843&status=done&style=none&taskId=uc280b434-eb97-465c-98b2-7de83bc1541&title=&width=584.5)
> router/index.js

```javascript
...
import VueRouter from 'vue-router'
...

//需要重写VueRouter.prototype原型对象身上的push|replace方法
//先把VueRouter.prototype身上的push|replace方法进行保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写VueRouter.prototype身上的push方法了
VueRouter.prototype.push = function(location, resolve, reject) {
  //第一个形参:路由跳转的配置对象(query|params)
  //第二个参数:undefined|箭头函数(成功的回调)
  //第三个参数:undefined|箭头函数(失败的回调)
  if (resolve && reject) {
    //push方法传递第二个参数|第三个参数(箭头函数)调用: this.push({},()=>{},()=>{})
    //originPush:利用call修改上下文,变为(路由组件.$router)这个对象,第二参数:配置对象、第三、第四个参数:成功和失败回调函数
    originPush.call(this, location, resolve, reject);
  } else {
    //push方法没有产地第二个参数|第三个参数
    originPush.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};

//重写VueRouter.prototype身上的replace方法了
VueRouter.prototype.replace = function(location, resolve, reject) {
  if (resolve && reject) {
    originReplace.call(this, location, resolve, reject);
  } else {
    originReplace.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};

10. 缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁(不会重新执行生命周期函数)。
  2. 如果缓存的组件需要重新发送请求, vue提供了两个生命周期函数image.png
  3. 场景:被切换的路由只要获取一次数据,不重复获取 或列表组件
  4. 具体编码:
    ```html <—! include=”组件名”—>

<—! 针对News组件—>

<—! 针对多个组件—>

<—! 针对非路由组件—>

<a name="DsgIb"></a>
#### 结合参数判断是否缓存
isAlive 为true时缓存组件
```javascript
// /route/index.js
const router =  new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About,
            meta:{isAlive:true}
        },
  ]
})
//About组件
<router-view v-if="!$route.meta.isAlive"></router-view>

<keep-alive> 
    <router-view v-if="$route.meta.isAlive"></router-view>
</keep-alive>

<keep-alive :include="$route.meta ? $route.name : ''"> 
    <router-view v-if="$route.meta.isAlive"></router-view>
</keep-alive>

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。
  3. 写法与 created一样
  4. 解析:

包裹的动态组件会被缓存,它是一个抽象组件,它自身不会渲染一个dom元素,也不会出现在父组件链中。当组件在 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
● 如包裹两个组件:组件A和组件B。当第一次切换到组件A时,组件A的created和activated生命周期函数都会被执行,这时通过点击事件改变组件A的文字的颜色,在切换到组件B,这时组件A的deactivated的生命周期函数会被触发;在切换回组件A,组件A的activated生命周期函数会被触发,但是它的created生命周期函数不会被触发了,而且A组件的文字颜色也是我们之前设置过的。

12.路由守卫

  1. 作用:对路由进行权限控制
  2. 分类:全局守卫、独享守卫、组件内守卫
  3. 参数:to(跳转到的路由),from(从哪跳转的),next(放行)

image.png

全局守卫:

// /route/index.js
const router =  new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About,
            meta:{isAuth:true,title:'新闻'}
        },
  ]
})

//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
    console.log('beforeEach',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
            next() //放行
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next() //放行
    }
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
    console.log('afterEach',to,from)
    if(to.meta.title){ 
        document.title = to.meta.title //修改网页的title
    }else{
        document.title = 'vue_test'
    }
})

独享守卫:

注意:

  1. 只有一个beforeEnter
  2. 写法“功能”与全局前置守卫一模一样

    const router =  new VueRouter({
     routes:[
         {
             name:'guanyu',
             path:'/about',
             component:About,
             meta:{isAuth:true,title:'新闻'},
       //独享守卫
       beforeEnter(to,from,next){
         console.log('beforeEnter',to,from)
         if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
           if(localStorage.getItem('school') === 'atguigu'){
             next()
           }else{
             alert('暂无权限查看')
             // next({name:'guanyu'})
           }
         }else{
           next()
         }
       }
         },
    ]
    })
    

    组件内守卫:

     export default {
         name:'About',
         //通过路由规则,进入该组件时被调用
         beforeRouteEnter (to, from, next) {
             next()
         },
    
         //通过路由规则,离开该组件时被调用
         beforeRouteLeave (to, from, next) {
             next()
         }
     }
    

13.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  1. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
      const router = new VueRouter({
      // mode: 'history',
      mode: 'hash',
      base: process.env.BASE_URL, //默认值: '/' ,应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"。
      routes
      })
      

      14.路由重定向

      ```javascript

//配置路由规则时 { path: ‘/‘, redirect: ‘/one’ }

<a name="wVeSg"></a>
### 15.动态添加路由规则 - addRoutes & addRoute
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1624878/1629724367478-d88caca7-4d3e-420d-ac13-669173b280b0.png#clientId=u559c4a36-4fb0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=356&id=u68d0f73f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=712&originWidth=1164&originalType=binary&ratio=1&rotation=0&showTitle=false&size=83043&status=done&style=none&taskId=u6e1249c7-90c4-42cf-8ae2-e40ce64531c&title=&width=582)
<a name="INhdw"></a>
#### 左导航栏根据token动态添加路由规则 (在根路由使用一次即可)
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1624878/1629724286039-0ff3cf59-c077-426f-9382-be2240e96be8.png#clientId=u559c4a36-4fb0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=271&id=u7a6a200b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=541&originWidth=1380&originalType=binary&ratio=1&rotation=0&showTitle=false&size=264623&status=done&style=none&taskId=ud8e2f4e2-9147-45be-8942-343ec2f1e38&title=&width=690)
<a name="W9Lkt"></a>
### 16.watch 监视路由更改- (路由组件守卫就能实现)
```javascript
let app = new Vue({
    el: '#app',
    watch: {
        "$route.path": function(newValue, oldvalue) {
            //newValue 跳转到的路由地址
            //oldvalue 跳转前的路由地址
            console.log(newValue, oldvalue)
        }
    },
    //4.挂载路由
    router: router,
    components: {
        "one": one,
        "two": two,
    }
});

17.路由滚动scroll

跳转路由界面后回到最上边

let router= new VueRouter({
  routes,
  //滚动行为
  scrollBehavior(to, from, savedPosition) {
    //返回的这个y=0,代表的滚动条在最上方
    //router4.x 以前写法
    return { y: 0 };
    //router4.x 写法
    return { top: 0 }
  },
})

export default router