Vue 用户管理系统(路由相关练习)
- [1. 内容介绍](#1. 内容介绍)
- [2. json-server(创建一个简单的后端服务)](#2. json-server(创建一个简单的后端服务))
- [3. 前端项目创建](#3. 前端项目创建)
-
- [3.1 axios 的使用](#3.1 axios 的使用)
- [3.2 使用路由](#3.2 使用路由)
-
- [3.2.1 组件和样式代码(直接复制就行,不是重点)](#3.2.1 组件和样式代码(直接复制就行,不是重点))
- [3.2.2 路由知识点讲解](#3.2.2 路由知识点讲解)
-
- [3.2.2.1 首页路由(修改为/home,匹配首页主体内容)](#3.2.2.1 首页路由(修改为/home,匹配首页主体内容))
- [3.2.2.2 动态路由匹配](#3.2.2.2 动态路由匹配)
- [3.2.2.3 路由嵌套](#3.2.2.3 路由嵌套)
- [3.3 组件中调用接口](#3.3 组件中调用接口)
1. 内容介绍
本篇文章介绍的系统练习,虽然作为 Vue Router 的练习,但是涉及的知识点还包括:
(1)json-server,模拟后端服务依赖;
(2)axios 封装,简便后端请求;
(3)Vue Router 的参数请求和嵌套请求。
2. json-server(创建一个简单的后端服务)
(1)创建一个文件夹 server(名字可自定义);
(2)cd server,使用 npm init -y 初始化 package.json。
package.json 内容如下:
javascript
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
(3)执行 npm i json-server,下载后端服务依赖;
(4)创建 db.json,作为后端服务内容:
javascript
{
"users": [
{
"id": "a3f9",
"name": "211",
"age": "2",
"phone": "2",
"email": "2",
"education": "小学",
"graduationschool": "2",
"profession": "2",
"profile": "2"
},
{
"id": "6639",
"name": "3",
"age": "3",
"phone": "3",
"email": "3",
"education": "博士",
"graduationschool": "3",
"profession": "3",
"profile": "3"
},
{
"id": "9aa0",
"name": "4",
"age": "4",
"phone": "4",
"email": "4",
"education": "本科",
"graduationschool": "4",
"profession": "4",
"profile": "4"
},
{
"id": "90f4",
"name": "1",
"age": "1",
"phone": "1",
"email": "1",
"education": "本科",
"graduationschool": "1",
"profession": "1",
"profile": "1"
},
{
"id": "4e7b",
"name": "22",
"age": "22",
"phone": "22",
"email": "22",
"education": "硕士",
"graduationschool": "22",
"profession": "22",
"profile": "22"
},
{
"id": "8444",
"name": "2",
"age": "2",
"phone": "2",
"email": "2",
"education": "本科",
"graduationschool": "2",
"profession": "2",
"profile": "2"
}
],
"companies": [
{
"id": "1",
"name": "Apple",
"description": "Apple is good!"
},
{
"id": "2",
"name": "Microsoft",
"description": "Microsoft is good!"
},
{
"id": "3",
"name": "Google",
"description": "Google is good!"
}
]
}
(5)修改 package.json 的 script 语句,关键代码:
javascript
"scripts": {
"json:server": "json-server --watch db.json"
},
完整 package.json:
javascript
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"json:server": "json-server --watch db.json"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"json-server": "^1.0.0-beta.3"
}
}
(6)执行 npm run json:server,启动后端服务

可以看到,服务启动后,将 users 和 companies 两个字段当成两个接口进行返回。
本次使用到的是 users,点击链接打开

(7)修改端)。
如果要启动多个 json-server 的项目,为避免端口冲突,可以修改package.json 中的命令中的端口。比如:
javascript
"scripts": {
"json:server": "json-server --port 3002 --watch db.json"
},
这样,端口就由默认的 3000修改为了 3002(测试完后记得修改回来)。
3. 前端项目创建
(1)创建一个项目,名为 user-app
javascript
npm create vue@latest

3.1 axios 的使用
(1)下载 axios:
javascript
npm i axios
(2)二次封装 axios。
创建一个 api/ request.js(对 axios 进行二次封装,主要是对请求和响应进行拦截处理)。代码如下:
javascript
// 该文件主要是对 axios 进行二次封装,主要是对请求和响应进行拦截处理
import axios from 'axios'
// 创建 axios 实例
const request = axios.create({
baseURL: 'http://localhost:3000',
timeout: 5000
})
// 请求拦截器
request.interceptors.request.use((config) => {
// config 就是你的请求
// 做一些其他的事情,比如给请求头添加 token
// 请求放行
return config
})
// 响应拦截器
request.interceptors.response.use(
(response) => {
// response 就是响应
// 做一些其他的事情,比如对响应结果进行处理
// 响应放行
return response
},
(error) => {
// 多了一个错误处理
return Promise.reject(error)
}
)
export default request
(3)创建api/userApi.js(封装具体的请求函数):
javascript
// 封装具体的请求函数
import request from './request.js'
/**
* 获取用户列表
*/
export function getUserListApi() {
return request({
url: '/users',
method: 'GET'
})
}
到这里,关于使用 axios 进行接口请求的函数已经可以进行使用。
3.2 使用路由
3.2.1 组件和样式代码(直接复制就行,不是重点)
(1)index.html 使用 CDN 的方式引入 bootstrap,用于样式修改:
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"
/>
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"
></script>
<script type="module" src="/src/main.js"></script>
</body>
</html>
(2)asset/main.css:
css
.content {
margin-top: 100px;
}
a {
text-decoration: none;
}
.navigation {
line-height: 50px;
margin: 0 10px;
font-weight: 400;
}
.navigation:hover {
color: #ccc;
text-decoration: none;
}
a.active {
color: #ccc;
text-decoration: none;
}
.table {
margin-top: 30px;
}
tr,
th {
text-align: center;
}
.glyphicon::before {
position: relative;
top: 2px;
margin-right: 10px;
}
(3)App.vue:
javascript
<template>
<div id="app" class="container">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#navbar"
aria-expanded="false"
aria-controls="navbar"
>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-brand">用户管理系统</div>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<router-link to="/home" class="navigation">主页</router-link>
<router-link to="/about" class="navigation">关于我们</router-link>
</ul>
<ul class="nav navbar-nav navbar-right">
<router-link to="/add" class="navigation">添加用户</router-link>
</ul>
</div>
</div>
</nav>
<!-- 由 vue-router 这个库提供的 -->
<!-- 路由所匹配上的组件,会渲染到这个位置 -->
<router-view class="content" />
</div>
</template>
<script setup></script>
<style scoped></style>
(4)views 文件夹中的各个组件。
Home.vue:
javascript
<template>
<div>
<h1>用户列表</h1>
<!-- 搜索框 -->
<input
type="text"
class="form-control"
placeholder="搜索内容"
v-model="searchItem"
@input="changeHandle"
/>
<!-- 表格:显示用户信息 -->
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>联系方式</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>{{ item.phone }}</td>
<td>
<router-link :to="`/detail/${item.id}`">详情</router-link>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from 'vue'
import { getUserListApi } from '../api/userApi.js'
const userList = ref([])
const searchItem = ref('')
const searchList = ref([]) // 存储搜索后的数据
onMounted(() => {
// 获取用户的数据
getUserListApi().then(({ data }) => {
userList.value = data
})
})
function changeHandle() {
// 根据用户输入的内容进行信息的过滤
searchList.value = userList.value.filter((item) => {
return item.name.includes(searchItem.value)
})
}
const list = computed(() => (searchItem.value ? searchList.value : userList.value))
</script>
<style scoped></style>
Detail.vue:
javascript
<template>
<div>Detail</div>
</template>
<script setup></script>
<style scoped></style>
About.vue:
javascript
<template>
<div class="about container">
<h1 class="page-header">使用说明</h1>
<p>通过此系统来熟悉 vue 以及 vue router 的使用</p>
<p>联系方式</p>
<router-link to="/about/email" class="navigation">邮箱</router-link>
<router-link to="/about/tel" class="navigation">电话</router-link>
<router-view />
</div>
</template>
<script setup></script>
<style scoped></style>
Email.vue:
javascript
<template>
<div>邮箱地址:xxx@.com</div>
</template>
Tel.vue:
javascript
<template>
<div>电话号码:131-xxxx-xxxx</div>
</template>
AddOrEdit.vue:
javascript
<template>
<div>Add or Edit</div>
</template>
<script setup></script>
<style scoped></style>
3.2.2 路由知识点讲解
router/index.js:
javascript
// 前端路由配置文件
import { createRouter, createWebHistory } from 'vue-router'
// 页面组件
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import AddOrEdit from '../views/AddOrEdit.vue'
import Detail from '../views/Detail.vue'
import Email from '../views/Email.vue'
import Tel from '../views/Tel.vue'
// 该方法会创建一个路由的实例
// 在创建路由实例的时候,可以传入一个配置对象
const router = createRouter({
history: createWebHistory(), // 指定前端路由的模式,常见的有 hash 和 history 两种模式
// 路由和组件的映射
routes: [
{
path: '/home', // 路由的路径
name: 'Home',
component: Home // 路由对应的组件
},
{
path: '/about',
name: 'About',
component: About,
// 创建嵌套的路由
children: [
{
path: 'email',
component: Email
},
{
path: 'tel',
component: Tel
},
{
path: '',
redirect: '/about/email'
}
]
},
{
path: '/add',
name: 'AddOrEdit',
component: AddOrEdit
},
{
// 这里的详情就应该是一个动态路由
path: '/detail/:id',
component: Detail
}
]
})
export default router
3.2.2.1 首页路由(修改为/home,匹配首页主体内容)
javascript
{
path: '/home', // 路由的路径
name: 'Home',
component: Home // 路由对应的组件
},

默认 path 通常为 '/',此时 App.vue 中的 router-view 并没有匹配内容。


但是我们这里修改为了
javascript
{
path: '/home', // 路由的路径
name: 'Home',
component: Home // 路由对应的组件
},
所以正式的首页地址应为 http://localhost:5173/home

3.2.2.2 动态路由匹配
路由后面跟着一个动态字段,比如id,我们称之为路径参数。比如:
javascript
{
// 这里的详情就应该是一个动态路由
path: '/detail/:id',
component: Detail
}
点击后,会跳转到对应的组件,在组件中可以通过 $route.params.id 进行获取。


3.2.2.3 路由嵌套
路由中,又嵌套一层或者多层路由,就称为路由嵌套。
javascript
{
path: '/about',
name: 'About',
component: About,
// 创建嵌套的路由
children: [
{
path: 'email',
component: Email
},
{
path: 'tel',
component: Tel
},
{
path: '',
redirect: '/about/email'
}
]
},


路由嵌套时的路由,通常为两层以上,比如: http://localhost:5173/about/email,此时后面的路由组件就会展示在上一层的 router-view 部分,可以通过 router-link 进行切换。
3.3 组件中调用接口
Home.vue 中的相关代码:
javascript
<script setup>
import { onMounted, ref, computed } from 'vue'
import { getUserListApi } from '../api/userApi.js'
const userList = ref([])
onMounted(() => {
// 获取用户的数据
getUserListApi().then(({ data }) => {
userList.value = data
})
})
</script>

让我们将关键代码进行改造:
javascript
onMounted(() => {
getUserListApi().then(res => {
console.log('res', res)
})
})

可以看到,返回的是一个对象,包括 status(请求结果状态)、statusText(请求结果状态文本)、data(请求返回结果)、headers(请求结果请求偷)等信息。
其中,我们通常只需要注意 status 和 data 两个字段即可。前者判断是否请求成功,后者是请求后返回的数据用于业务和视图的处理。
上一章 《Vue3 路由介绍》