Vue 用户管理系统(路由相关练习)

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 路由介绍

相关推荐
apollo_qwe3 小时前
封装axios实现全局loading,在一定程度上减少重复请求的发生
axios
李剑一4 小时前
前端实现时间轴组件拼接N多个不连续监控视频展示
前端·vue.js
@大迁世界4 小时前
第06章:Dynamic Components(动态组件)
前端·javascript·vue.js·前端框架·ecmascript
Revol_C5 小时前
【Element Plus】升级版本后,el-drawer自定义的关闭按钮离奇消失之谜
前端·css·vue.js
caicai_lf_niuniu5 小时前
🌳 ComboTreeV2:高性能虚拟树
前端·vue.js
梵得儿SHI5 小时前
Vue 核心语法详解:模板语法中的绑定表达式与过滤器(附 Vue3 替代方案)
前端·javascript·vue.js·插值语法·vue模板语法·绑定表达式·过滤器机制
岁月宁静6 小时前
大规模图片列表性能优化:基于 IntersectionObserver 的懒加载与滚动加载方案
前端·javascript·vue.js
一 乐6 小时前
医疗保健|医疗养老|基于Java+vue的医疗保健系统(源码+数据库+文档)
java·前端·数据库·vue.js·毕设
Sheldon一蓑烟雨任平生14 小时前
Vue3 插件(可选独立模块复用)
vue.js·vue3·插件·vue3 插件·可选独立模块·插件使用方式·插件中的依赖注入