Vue15-路由(vue-router)

 


Vue15-路由(vue-router)

1.路由简介

  1. vue-router(路由)是vue中的一个插件库,专门用来实现SPA应用。
  2. SPA简介。
    1. SPA,Single Page Web Application,即单页面Web应用,整个应用只有一个完整的页面。
    2. SPA应用中点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
    3. 数据需要通过ajax请求获取。
  3. 路由就是一组映射关系,key和value的映射关系。
  4. 路由分为前端路由和后端路由。
    1. 前端路由的value是Component组件,当浏览器的路径改变时, 对应的组件就会显示。
    2. 后端路由的value是function函数,用于处理客户端提交的请求。服务器接收到请求后, 根据请求路径找到对应的函数来处理请求,处理完成返回响应的数据。

2.路由的安装和使用

  1. 安装vue-router,npm i vue-router@3。vue当前版本是3,对应的vue-router版本是4;vue-router4需要在vue3中使用;vue2需要使用vue-router3,所以使用vue-router@3引入vue-router的3版本。
  2. 路由案例需要引入bootstrap.css。引入方式:public下创建css文件夹,css文件夹中放入bootstrap.css,然后在public/index.html中引入bootstrap.css。
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
  1. 创建三个基本的组件。
<!-- 1 普通组件Banner.vue,src/compontent/Banner.vue -->
<template>
    <h1>Vue Router Demo</h1>
</template>

<script>
    export default {
        name: "Banner"
    }
</script>

<!-- 2 路由组件About.vue,src/pages/About.vue -->
<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
    name: "About",
}
</script>

<!-- 3 路由组件Home.vue,src/pages/Home.vue -->
<template>
  <h2>我是Home的内容</h2>
</template>

<script>
export default {
    name: "Home",
}
</script>
  1. 创建路由。
// src下创建router文件夹,router下创建index.js,index.js文件的内容如下。
// 这个文件用来创建路由
import VueRouter from 'vue-router'
import About from '../pages/About'
import Home from '../pages/Home'

export default new VueRouter({
    routes: [
        {
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home
        }
    ]
});
  1. main.js中引入和使用路由插件。
import Vue from 'vue'
import App from './App'
import VueRouter from 'vue-router'
import router from './router'

// 使用路由,当使用后Vue上就会多一个router配置项。
Vue.use(VueRouter);
new Vue({
    el: '#app',
    render: h => h(App),
    router: router,
});
  1. App.vue定义路由的展示位置。
<template>
    <div>
        <div class="row">
            <div class="col-xs-offset-2 col-xs-8">
                <div class="page-header">
                    <Banner></Banner>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!-- 原始HTML使用a标签实现页面跳转 -->
                    <!--<a class="list-group-item active" href="./about.html">About</a>-->
                    <!--<a class="list-group-item" href="./home.html">Home</a>-->

                    <!-- 路由的工作过程,当点击About后,去router/index.js中寻找路径为/about的路径,
                            返回/about路由匹配到About组件,然后将About组件展示到<router-view/>位置。
                     -->
                    <!-- vue中使用router-link标签实现路由的切换,router-link最终会被解析为a标签。 -->
                    <!-- active-class,当路径是/about.html时,显示active属性。 -->
                    <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
                    <!-- active-class,当路径是/home.html时,显示active属性。 -->
                    <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
                </div>
            </div>
            <div class="col-xs-6">
                <div class="panel">
                    <div class="panel-body">
                        <!-- router-view指定路由切换时组件的展示位置。 -->
                        <router-view/>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import Banner from "./components/Banner";
    export default {
        name: "App",
        components: {Banner}
    }
</script>

3.路由补充

  1. 组件的分类。普通组件,普通组件一般放在components文件夹下;路由组件,路由组件一般放在pages文件夹下。
  2. 当切换路由时,隐藏的组件,默认是被销毁的。当需要的时候在去创建并挂载。
  3. 每个路由组件都有自己的$route属性,里面存储自己的路由信息。
  4. 整个应用只有一个路由器,可以通过$router获取。

4.多级(嵌套)路由

  1. 通过在src/router/index.js中配置children配置项来实现多级路由。
  2. 多级路由案例中main.js、App.vue、Banner.vue、About.vue代码不变,新增Message.vue和News.vue组件,需要修改src/router/index.js和Home.vue文件。
  3. src/router/index.js中配置多级路由。
import VueRouter from 'vue-router'
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'

export default new VueRouter({
    routes: [
        {
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            children: [ // 通过children配置项,配置多级路由。
                {
                    path: 'news', // 多级路由中不要写为/news,即路由匹配时会自动加/,所以不写/。
                    component: News
                },
                {
                    path: 'message',
                    component: Message
                }
            ]
        }
    ]
});
  1. Home.vue中实现路由的切换。
<template>
    <div>
        <h2>我是Home的内容</h2>
        <div>
            <ul class="nav nav-tabs">
                <li>
                    <!-- 多级路由的切换需要在router-link写全路径,即to="/home/news"。 -->
                    <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
                </li>
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
                </li>
            </ul>
            <router-view></router-view>
        </div>
    </div>
</template>
  1. 新增的Message.vue和News.vue。
<!-- Message.vue -->
<template>
  <div>
    <ul>
      <li>
        <a href="/message1">message001</a>&nbsp;&nbsp;
      </li>
    </ul>
  </div>
</template>

<script>
    export default {
        name: "Message"
    }
</script>

<!-- News.vue -->
<template>
  <ul>
    <li>news001</li>
  </ul>
</template>

<script>
export default {
  name: "News"
}
</script>

5.路由传参-query形式传参

  1. 路由传参-query形式传参案例中main.js、router/index.js、App.vue、Banner.vue、About.vue、Home.vue、News.vue中代码不变,新增Detail.vue组件,需要修改Message.vue文件。
  2. Message.vue中使用路由并传参。
<template>
    <div>
        <ul>
            <li v-for="message in messageList" :key="message.id">
                <!-- 路由传出,to的字符串形式。 -->
                <!-- to需要解析message.id,所以前需要变为:to。单个字符串不能是js表达式,所以需要使用模板字符串。 -->
                <!-- 传递的参数会被封装到请求后,即请求为http://localhost:8080/#/home/message/detail?id=A003&title=%E6%B6%88%E6%81%AF4。 -->
                <!-- 组件中可以使用this.$route.query获取到传递的参数。 -->
                <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`">{{message.title}}</router-link>

                <!-- 路由传出,to的对象。 -->
                <router-link :to="{
                    path: '/home/message/detail',
                    query: {
                        id: message.id,
                        title: message.title
                    }
                }">
                    {{message.title}}
                </router-link>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "Message",
    data() {
        return {
            messageList: [
                {
                    id: 'A001',
                    title: '消息1'
                },
                {
                    id: 'A002',
                    title: '消息2'
                },
                {
                    id: 'A003',
                    title: '消息3'
                }
            ]
        }
    }

}
</script>
  1. Detail.vue中获取Message.vue中传递的参数。
<template>
    <ul>
        <li>消息id {{$route.query.id}}</li>
        <li>消息title {{$route.query.title}}</li>
    </ul>
</template>

<script>
    export default {
        name: "Detail",
        mounted() {
            // this.$route.query是一个对象。
            console.log(this.$route.query)
        }
    }
</script>

6.命名路由

  1. 命名路由可以简化路由的跳转。使用命名路由时需要在router/index.js中使用name配置项给路径定义名称。

  2. 命名路由案例需要修改router/index.js、App.vue和Message.vue。

  3. router/index.js,router/index.js省略了大部分的配置项。

export default new VueRouter({
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            children: [ // 通过children配置项,配置多级路由。
                {
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component: Detail
                        }
                    ]
                }
            ]
        }
    ]
});
  1. App.vue。
<!-- 当使用命名路由后,:to需要使用对象写法。 -->
<router-link class="list-group-item" active-class="active" :to="{name: 'guanyu'}">About</router-link>
  1. Message.vue。
<router-link :to="{
                      name: 'xiangqing',
                      query: {
                          id: message.id,
                          title: message.title
                      }
                  }">
    {{message.title}}
</router-link>

7.路由传参-params形式传参

  1. 路由传参-params形式传参案例需要修改router/index.js和Message.vue。
  2. router/index.js中使用占位符定义参数。
path: 'message',
component: Message,
children: [
    {
        name: 'xiangqing',
        path: 'detail/:id/:title', // path中使用占位符声明接受params参数。
        component: Detail
    }
]
  1. Message.vue中传递参数。
<template>
    <div>
        <ul>
            <li v-for="message in messageList" :key="message.id">
                <!-- 路由传参,params形式传参,to的字符串形式。 -->
                <router-link :to="`/home/message/detail/${message.id}/${message.title}`">{{message.title}}</router-link>

                <!-- 路由传参,to的对象形式。 -->
                <router-link :to="{
                    name: 'xiangqing',
                    params: {
                        id: message.id,
                        title: message.title
                    }
                }">
                    {{message.title}}
                </router-link>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>
  1. Detail.vue中接受参数。
<template>
    <ul>
        <li>消息id {{$route.params.id}}</li>
        <li>消息title {{$route.params.title}}</li>
    </ul>
</template>

8.路由传参-props配置项

  1. props配置项可以让路由组件更方便的接受到参数。没有使用props接受参数:$route.params.id;使用props配置项后接受参数:props中定义参数props: ['id', 'title']();差值表达式中直接使用参数{{id}}。
  2. 路由传参-props配置项案例需要修改router/index.js和Detail.vue。
  3. router/index.js中引入props配置项,props有三种配置方式。
// 1 props第一种写法,值为对象。
path: 'message',
component: Message,
children: [
    {
        name: 'xiangqing',
        path: 'detail/:id/:title',
        component: Detail,
        // props的第一种写法,值为对象。这种写法只能传递固定数据。
        // props对象中的key-value都会以props的形式传递给Detail组件。
        props: {
            a: 1,
            b: 'hello'
        }
    }
]

// 2 props第二种写法,值为布尔值。
// 如果布尔值为真,就会把该路由组件收到的所有params形式的参数,
// 以props的形式传递给Detail组件。
// props: true

// 3 props第三种写法,值为函数。
// 当props使用函数写法时,函数会接受到route参数。
// props(route) {
//     return {
//         id: route.params.id,
//         title: route.params.title
//     }
// }

// props值为函数的简单写法,使用对象的结构化赋值。
// props({params}) {
//     return {id: params.id, title: params.title}
// }

// 对象的结构化赋值的简单版。
props({params:{id, title}}) {
    return {id, title}
}
  1. Detail.vue组件中接受参数。
<template>
    <ul>
        <!-- 没有使用props配置项的写法 -->
        <li>消息id {{$route.params.id}}</li>
        
        <!-- 使用props配置项后的写法。 -->
        <li>消息id {{id}}</li>
        <li>消息title {{title}}</li>
    </ul>
</template>

<script>
    export default {
        name: "Detail",
        // props中定义接受的参数。
        props: ['id', 'title']
    }
</script>

9.router-link标签中的replace属性

  1. 浏览器历史记录的写入可以简单看做是对栈的操作。浏览器历史记录有两种写入方式:push和replace。
  2. push是在栈顶追加历史记录,模式路由跳转时默认使用的是push模式。
  3. replace会替换当前历史记录,即跳转操作不会写入到浏览器历史记录中。
  4. 开启replace模式。
<!-- router-link标签中添加replace属性表示开启replace模式。 -->
<router-link replace>Home</router-link>

10.编程时路由导航

  1. 编程式路由导航可以在不借助router-link标签的情况下实现路由跳转。
  2. 编程时路由导航案例需要修改Message.vue。
<template>
    <div>
        <ul>
            <li v-for="message in messageList" :key="message.id">
                </router-link>
                <button @click="pushShow(message)">push查看</button>
                <button @click="replaceShow(message)">replace查看</button>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "Message",
    methods: {
        pushShow(message) {
            // push模式路由跳转,需要传递一个配置对象参数。
            // name,通过name属性在router/index.js中匹配跳转的路由。
            // params是路由跳转是携带的参数。
            this.$router.push({
                name: 'xiangqing',
                params: {
                    id: message.id,
                    title: message.title
                }
            })
        },
        // replace模式路由跳转,需要传递一个配置对象作为参数。
        replaceShow(message) {
            this.$router.replace({
                name: 'xiangqing',
                params: {
                    id: message.id,
                    title: message.title
                }
            });
        }
    }
}
</script>
  1. 编程时路由的其他跳转方式。
this.$router.back(); // 后退
this.$router.forward(); // 前进
this.$router.go(-2); // 后退两步。go可以前进,也可以后退。

11.缓存路由组件

  1. 缓存路由组件可以让不展示的组件保持挂载,即组件被隐藏了也不会被销毁。
  2. 缓存路由组件需要修改Home.vue文件。
<!-- 1 使用keep-alive来缓存组件,keep-alive中的组件都会被缓存。
	 2 可以通过配置keep-alive中的include属性来定义需要被缓存的组件,include中指定组件名称。
-->
<keep-alive include="News">
    <router-view></router-view>
</keep-alive>
<!-- 3 缓存多个组件的写法。 -->
<keep-alive :include="['News', 'Message']"></keep-alive>

12.路由组件中两个独有的生命周期函数

  1. activated和deactivated是路由组件独有的生命周期钩子,用于捕获路由组件的激活状态。
  2. activated在路由组件被激活时触发,即路由组件显示在页面中触发。
  3. deactivated在路由组件失活时触发,即路由组件从页面中隐藏时触发。
  4. 路由组件中两个独有的生命周期函数案例需要修改News.vue文件。
<template>
    <ul>
        <li :style="{opacity}">消息</li>
    </ul>
</template>
<script>
export default {
    // 当路由组件被缓存之后,路由组件的显示和隐藏并不会调用mounted()和beforeDestroy()。
    // 需要使用路由组件独有的生命周期函数activated和deactivated。
    // activated()函数会在路由组件被显示在页面上时调用。
    // deactivated()函数会在路由组件从页面隐藏时调用。

    // activated中定义定时器。
    activated() {
        this.timer = setInterval(() => {
            this.opacity -= 0.1;
            if (this.opacity <= 0) {
                this.opacity = 1;
            }
        }, 100);
    },
    // deactivated中清除定时器。
    deactivated() {
            clearInterval(this.timer);
    }
}
</script>

13.路由守卫

  1. 路由守卫用来对路由进行权限控制。
  2. 路由守卫可以分为:全局守卫、独享守卫和组件内守卫。

14.全局路由守卫

  1. 全局路由守卫可以分为:全局前置路由守卫和全局后置路由守卫。
  2. 全局路由守卫案例只需要修改router/index.js文件。
const router = new VueRouter({
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About,
            // meta中可以存放路由元信息,即路由的其他的信息,如isAuth是否需要校验。
            meta: { 
                title: '关于',
                isAuth: true,
            }
        },
    ]
});

// 全局前置路由守卫。
// 全局前置路由守卫在初始化的的时候会被调用或者每次路由切换前被调用。
// to, from, next参数的含义:from 从哪个路由来;to 到哪个路由去;
//      next是一个函数,当路由放行时调用。
router.beforeEach((to, from, next) => {
    // 当isAuth为true时表示需要进行路由的校验,否则不需要进行校验,直接通过next()函数放行。
    if (to.meta.isAuth) {
        if (localStorage.getItem('name') === 'tom') {
            next();
        } else {
            alert('没有权限查看!');
        }
    } else {
        next();
    }
});

// 全局后置路由守卫。
// 全局后置路由守卫在初始化的的时候会被调用,或者每次路由切换后被调用。
router.afterEach((to, from) =>  {
    // 后置路由守卫可以用来在路由切换后切换网页的title页签。
    document.title = to.meta.title || '系统';
});
export default router

15.独享路由守卫

  1. 独享路由守卫只有beforeEnter,即只会在进入之前调用beforeEnter函数。
  2. 独享路由守卫在路由内定义。
  3. 独享路由守卫案例需要修改router/index.js文件。
const router = new VueRouter({
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About,
            meta: {
                title: '关于',
                isAuth: true
            },
            // 独享路由守卫
            beforeEnter(to, from, next) {
                if (to.meta.isAuth) {
                    if (localStorage.getItem('name') === 'tom') {
                        next();
                    } else {
                        alert('没有权限查看!');
                    }
                } else {
                    next();
                }
            }
        }
    ]
});
export default router

16.组件内路由守卫

  1. 组件内路由守卫通过beforeRouteEnter()和beforeRouteLeave()函数来实现。
  2. beforeRouteEnter()在通过路由规则进入该组件时调用;beforeRouteLeave()在通过路由规则离开该组件时调用。
  3. 组件内路由守卫案例需要修改About.vue文件。
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name: "About",
    // 组件内路内守卫,只是在通过路由进入该组件时触发。
    // 组件的其他引入方式,如通过标签<About>引入时不会触发组件内路由守卫。
    // 进入该组件时,to是该组件的路由,离开时from是该组件的路由。
    // beforeRouteEnter在通过路由规则进入该组件时调用。
    beforeRouteEnter(to, from, next) {
        next();
    },
    // beforeRouteLeave在通过路由规则离开该组件时调用。
    beforeRouteLeave(to, from, next) {
        next();
    }
}
</script>

17.路由的两种工作模式

  1. 路由的第一种工作模式:hash。hash是路由默认的工作模式,路径中会携带#,在请求服务器时#后的内容不会被传递过去,#及其后面的内容就是hash值。
  2. 路由的第二种工作模式:history。history工作模式下路径中不会携带#,但是当使用history模式时,点击刷新,发送网络,请求会出现404的问题。
  3. hash模式的优缺点。
    1. hash模式的缺点:地址中带#,不美观;地址通过app分享,若app校验严格,则地址会被标记为不合法。
    2. hash模式的优点:兼容性好。
  4. history模式的优缺点。
    1. history模式的缺点:兼容性和hash模式相比略差;应用部署上线时需要后端人员支持解决页面刷新出现404的问题。
    2. history模式的优点:地址干净。
  5. 路由的两种工作模式案例需要修改router/index.js文件。
const router = new VueRouter({
    // 从默认
    mode: 'history',
});
posted @   行稳致远方  阅读(45)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示