Vue-Router:单页应用拥有多个页面的秘诀

430 阅读13分钟

1. 引言

我们上次说过,Vue框架实现的是单页应用,整个项目只有一个html文件,我们在vue文件中写代码片段然后引入html页面中展示。但一个真正的项目不会只有一个页面的,在原生js中我们通过写很多份html文件来实现页面跳转,那在Vue里面怎么实现呢?

思维活跃的朋友可能就会想到,我写很多个代码片段,将这个代码片段作为首页,将那个代码片段作为登录。当我点首页的时候就把首页的代码片段拿来展示,登录的代码片段就不展示。其实这样是可以的,实现了页面的跳转。但我们说,一个页面一定是和一个url强绑定的,意思就是当我点击首页,url就会加上‘/index’然后给我跳转到首页的界面,每一个页面都有自己对应的url。我们观察一个网站能发现其实它就是这样做的。如果我们按第一种方法来做,那至始至终都只有一个url,只是代码片段切换了,这样做就不方便了,当我们想为别人展示我们网站的一个页面时,我们还得从最开始的那个状态然后去展示代码片段。而一个页面对于一个url的话,我们只要正确输入页面所对应的url就能去到这个页面。

所以说了这么一大堆,怎么才能实现一个页面对于一个url呢?那就轮到我们今天的主角:Vue-Router 闪亮登场了。

2. Vue-Router

我们还是使用vite这个小工具来创建我们的项目,我们就能得到这样一个目录结构:

image.png

我们可以去Vue-Router的官方文档上看看如何使用。Vue-Router

我们在终端中输入以下指令进行安装:

npm install vue-router@4

我们来实现一个后台管理系统的小demo,这个demo功能和界面非常简陋,主要只是为了展示页面跳转的效果。

第一个页面我们就写一个登录页面,login。当用户输入账号和密码时就跳转到首页,也就是home页面。实现一个跳转效果,并且url也要改变。

所以,我们在src下新建一个views文件夹,我们把这个项目中所有的页面写到这个文件夹下。我们创建一个Login.vue, 我们就将这个代码片段作为登录页面,我们先简单的写上:

<template>
    <div>
        <h1>login page</h1>
    </div>
</template>

<script setup>

</script>

<style lang="css" scoped>

</style>

我们希望在页面上看到‘login page’,按照我们之前的写法,就是将它写成一个组件,然后引入到App.vue中。但在这里,我们要将它作为一个页面来展示,就需要为它配一个路由。

那我们再来到src文件夹下新建一个router文件夹,在router文件夹下新建一个index.js文件,在这里来进行路由的配置。

现在目录结构就长这样:

image.png

好,来到index.js文件中,我们已经安装了Vue-Router了,那应该怎么为Login.vue配置一个路由呢?

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: routes
})

export default router;

我们需要先从vue-router中引入两个函数 createRouter, createWebHistory。createRouter函数的调用可以创建一个路由的实例对象,这个函数接收一个对象作为参数,我们要放上两个属性:history和routes,routes的值是一个数组,需要我们自己创建,最后我们将这个router抛出。

我们就在数组routes中进行路由的配置,我们在这个数组中放一个对象。

import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'

const routes = [
    {
        path: '/login',
        component: Login
    }
]

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: routes
})

export default router;

这个对象我们放两个属性 path和component。path就是这个页面所拥有的url,component就应该是我们写的那个login页面了,于是我们要将Login.vue引入,import Login from '../views/Login.vue'。这样当浏览器读到了'/login'路径时,它就会将Login组件加载出来,也就是我们写的login页面。

那这样就搞定了吗?还没有,这个文件只是router文件夹下的一个文件,它并没有和其它东西产生关联。我们知道,我们是将App.vue里的东西拿到html文件中进行展示了,而我们是在main.js中完成这个操作的。所以在这里我们抛出了一个router,我们来到main.js中接收它。

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index.js'

createApp(App).use(router).mount('#app')

引入之后我们就要在createApp(App)后面接‘.use(router)’去使用,这样就是App使用了router,配置了路由。这样还没完,我们还需要指明Login加载到哪里,因为是App.vue里的东西会被拿到页面中展示,所以我们还要在App.vue中这样干:

<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script setup>

</script>

<style lang="css" scoped></style>

现在,我们写的这个Login.vue就是一个页面了,它已经配好了路由,它会有自己对应的url。当我们输入这个url,就能跳转到这个页面。

PixPin_2024-12-17_09-26-16.gif

好,现在我们已经为这个页面配好了路由,接下来,就该实现我们的登录功能了。

html和css部分比较简单,我们直接跳过,我们主要来看js部分。

<template>
    <div class="login">
        <h2>旅梦的后台管理系统</h2>
        <div class="login-box">
            <input type="text">
            <input type="text">
            <button>登陆</button>
        </div>
    </div>
</template>

<script setup>

</script>

<style lang="css" scoped>
.login {
    width: 400px;
    border: 1px solid #000;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.login-box {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.login-box input {
    margin: 10px 0;
    padding: 5px 10px;
}

.login-box button {
    padding: 5px 20px;
}
</style>

页面现在就长这样:

image.png

我们希望当用户输入正确的账号和密码时,点击登录就会跳转到首页,也就是home页面。

我们需要获取用户在input框输入的内容,可以使用v-model,并且要为button绑定一个点击事件。这里我们就写简单一点,我们直接对用户的输入进行判断。当用户输入admin和123时,我们就进行跳转。

<template>
    <div class="login">
        <h2>旅梦的后台管理系统</h2>
        <div class="login-box">
            <input type="text" v-model="username">
            <input type="text" v-model="password">
            <button @click="login">登陆</button>
        </div>
    </div>
</template>

<script setup>
import { ref } from "vue";

const username = ref("admin");
const password = ref("123");

const login = () => {
    if (username.value === 'admin' && password.value === '123') {
            // 修改 url 地址栏为 /home

    
    }
}
</script>

<style lang="css" scoped>
.login {
    width: 400px;
    border: 1px solid #000;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.login-box {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.login-box input {
    margin: 10px 0;
    padding: 5px 10px;
}

.login-box button {
    padding: 5px 20px;
}
</style>

我们将username和password设置了响应式,使用了ref,所以我们想使用它们的值就必须加 ‘.value'。这里我们可以直接就让username和password为admin和123,省的我们每次去输入。然后在login函数中进行判断,当用户输入admin和123时,就要进行页面的跳转,也就是要修改 url 地址栏为 /home。那怎么去修改呢?

我们需要从vue-router中引入一个函数useRouter,调用它创建一个实例对象router。请注意。这里是一个单例模式,也就是说,这个router和index.js中的router是同一个。然后router身上有一个方法push,我们就这样修改url。

<template>
    <div class="login">
        <h2>旅梦的后台管理系统</h2>
        <div class="login-box">
            <input type="text" v-model="username">
            <input type="text" v-model="password">
            <button @click="login">登陆</button>
        </div>
    </div>
</template>

<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";

const username = ref("admin");
const password = ref("123");
const router = useRouter()

const login = () => {
    if (username.value === 'admin' && password.value === '123') {
        // 修改 url 地址栏为 /home
        router.push({ path: '/home'});
    }
}
</script>

<style lang="css" scoped>
.login {
    width: 400px;
    border: 1px solid #000;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.login-box {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.login-box input {
    margin: 10px 0;
    padding: 5px 10px;
}

.login-box button {
    padding: 5px 20px;
}
</style>

push方法接收一个对象,里面放上一个属性path为'/home'。此时,当我们点击登录按钮时,浏览器就会跳转到home页面,虽然此时我们还没有写home页面。我们来看一下有没有这个效果。

PixPin_2024-12-17_09-51-30.gif

浏览器的url栏确实跳转到了'/home',这样就可以实现页面的跳转。

接下来,我们就该着手去写home页面,并为它配一个路由,url就要为'/home'。

我们还是来到views文件夹下,创建一个Home.vue文件。

<template>
    <div>
        <h1>home page</h1>
    </div>
</template>

<script setup>

</script>

<style lang="css" scoped>

</style>

先写上一点东西,home page。现在我们就希望,当url为'/home',这个home page能在页面中展示。于是我们就得为它配一个路由,我们来到index.js文件下,为它配一个路由。

import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'

const routes = [
    {
        path: '/login',
        component: Login
    },
    {
        path: '/home',
        component: () => import('../views/Home.vue'),
    }
]

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: routes
})

export default router;

我们直接在数组中再加一项,就和第一项一样,我们还可以简写一下,直接在component中引入Home.vue。这样写有一个好处,当我们将引入语句全部写在开头时,一打开页面就会全部开始加载,而这样写,只有来到'/home'下才开始加载,分担一下压力。

这样我们就为home页面配好了一个路由,我们来看看效果。

PixPin_2024-12-17_10-10-59.gif

实现了我们想要的效果。那我们就开始写home页面。

<template>
    <div class="home">
        <div class="head">
            <div class="logo">旅梦的后台管理系统</div>
            <div class="user">
                <span>Welcome, {{ route.query.name }}</span>
            </div>
        </div>
        <div class="main">
            <div class="menu">
                <ul>
                    <li>
                        <router-link to="/home/user">用户管理</router-link>
                    </li>
                    <li>
                        <router-link to="/article">文章管理</router-link>
                    </li>
                    <li>
                        <router-link to="/catgory">评论管理</router-link>
                    </li>
                </ul>
            </div>
            <div class="content">
            </div>
        </div>
    </div>
</template>

<script setup>

import { useRoute } from 'vue-router';
const route = useRoute();

</script>

<style lang="css" scoped>
.head {
    height: 60px;
    display: flex;
    justify-content: space-between;
    padding: 0 30px;
    align-items: center;
    background-color: #4e12d871;
    color: #fff;
}

.main {
    display: flex;
    height: calc(100vh - 60px)
}

.menu {
    flex: 0 0 200px;
    /* 不放大 不缩小 */
    background-color: #6453e8;
}

.menu ul {
    list-style: none;
    text-align: center;
}

.menu ul li a {
    display: block;
    padding: 15px 0;
    color: #fff;
    cursor: pointer;
}

.content {
    flex: 1
}
</style>

我们在span标签中写了Welcome, {{ route.query.name }},这是干嘛的呢?其实这样写能实现一个效果,当用户是admin时,我们就欢迎admin;当用户是张三时,我们就欢迎张三。那怎么去获取这个用户名呢?用户名是在Login.vue中的,我们需要在Home.vue也使用它。

我们可以这样引入,在Login.vue中修改url时,再加上一个query,里面放上一个对象,里面放上一个属性name,这个属性名可以自己取,然后值为username.value。

const login = () => {
    if (username.value === 'admin' && password.value === '123') {
        // 修改 url 地址栏为 /home
        router.push({ path: '/home', query: { name: username.value } });
    }
}

这样当我们跳转到 '/home'时,就能带过去一个值。它会被拼接在url上。

PixPin_2024-12-17_10-33-35.png

这样我们就能实现从其它页面引入一个值放到这个页面来使用。那怎么使用这个值呢?

我们需要从vue-router中引入一个函数useRoute,调用它创建一个实例对象route。

import { useRoute } from 'vue-router';
const route = useRoute();

注意这里和Login.vue中的区别。我们在Login.vue中是使用了useRouter创建了router,这里是使用了useRoute创建了一个route,少了一个'r'你发现没有。这里很容易搞混。引入完之后,我们就可以route.query.name去使用这个值了,我们将它放在span标签里。

现在页面就长这样,右上角欢迎就能欢迎在login页面输入的用户名:

image.png

我们还有一种方法能从其它页面带一个值过来,简单提一下。我们这样写:

const login = () => {
    if (username.value === 'admin' && password.value === '123') {
        // 修改 url 地址栏为 /home
        //router.push({ path: '/home', query: { name: username.value } });
        router.push({ name: 'Home', params: { name: username.value } });
    }
}

使用name和params。这样写的话,当我们在配置路由时还要增加一个属性name:

在index.js中:

const routes = [
    {
        path: '/login',
        name: 'Login',
        component: Login
    },
    {
        path: '/home/:name',
        name: 'Home',
        component: () => import('../views/Home.vue'),
    }
]

并且还要在path中这样写:'/home/:name',允许它带一个参数过来。这样我们也从Login.vue引入了一个值过来,此时我们就要这样使用:route.params.name。这里我们简单提一下。

这里我们还是使用第一种方法。

还是回到这个home页面上:

image.png

当我们点击左边用户管理、文章管理和评论管理时,也应该去到各自的页面,那就也要修改url地址,就可以使用router-link标签,然后在to中写对应的路径。

<ul>
    <li>
        <router-link to="/home/user">用户管理</router-link>
    </li>
    <li>
        <router-link to="/article">文章管理</router-link>
    </li>
    <li>
        <router-link to="/catgory">评论管理</router-link>
    </li>
</ul>

其实router-link标签就是封装了a标签给我们使用。这样当我们点击用户管理时,也能去修改url了。

PixPin_2024-12-17_11-46-18.gif

这样也就能实现页面的跳转。但在这里,按照一个通常的后台管理系统来说,当我们点击用户管理时,内容应该出现在右边的白色区别吧。也就是什么意思呢,如果它是跳转到另一个页面了。这个组件就是加载到App.vue中去了。但这里我们想把用户管理这个组件加载到Home.vue中,于是也要为用户管理页面配一个路由,此时就是要在'/home'路径下配一个子路由了。

我们先来到views文件夹下创建一个User.vue文件,用它来作为用户管理的页面。然后来到index.js中为它配一个路由,此时就不能单独给它配一个路由了,而是要配一个子路由:

import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
  
const routes = [
    {
        path: '/login',
        component: Login
    },
    {
        path: '/home',
        component: () => import('../views/Home.vue'),
        children: [
            {
                path: '/home/user',
                component: () => import('../views/User.vue')
            },
        ]
    }
]

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: routes
})

export default router;

在第二个对象里面放一个 children属性,也是一个数组,里面放上一个对象,为它配上路由。

配好了路由之后,我们就要将User组件加载Home组件里,就像当时Login组件要加载到App里一样。我们希望它展示在content容器里,于是我们这样写:

<div class="content">
    <router-view></router-view>
</div>

当我们点击了用户管理后它就会被放到content容器中展示,也就是那块白色区域。

PixPin_2024-12-17_12-01-02.gif

这样就实现了我们想要的效果。

还有个小细节要提,就是当用户在login页面登陆来到home页面时,默认就先要为用户展示用户管理页面吧,而不需要用户再去点击用户管理。你不能当用户来到home页面时为用户展示一片空白啊。

所以我们要在children中这样写:

import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
  
const routes = [
    {
        path: '/login',
        component: Login
    },
    {
        path: '/home',
        component: () => import('../views/Home.vue'),
        children: [
            {
                path: '/home/user',
                component: () => import('../views/User.vue')
            },
            {
                path: '',
                redirect: '/home/user'
            },
        ]
    }
]

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: routes
})

export default router;

redirect属性,当用户来到'/home'路径时,直接为用户展示'/home/user'。

我们来看一下效果:

PixPin_2024-12-17_12-15-39.gif

这样就在home页面默认展示了用户管理页面,不需要用户再去点击了。

3. 总结

这样,我们大概学了一下Vue-Router的一些简单用法,我们来做个总结:

路由是指根据不同的URL请求,分配展示不同的 vue 组件的过程

router 只需要配置 path 和 component 的映射关系

router.push() 可以跳转到指定的路由

router.push({path:'/xxx',query:{xxx:xxx}}) 可以携带参数跳转

useRouter 可以获取路由的实例对象,它和router 文件下的 index.js中的路由对象是同一个

useRoute 可以获取当前路由的信息

  • 二级路由的配置

将某个子路由配置在该路由的 children中,在该路由的组件中通过 router-view 渲染出来子路由的组件

这是一些基本的用法,能为页面配置路由,能实现页面的跳转,修改url地址。

如果对你有帮助的话不妨点个赞吧。