Vue 3 项目实战教程大事件管理系统 (一):从零开始搭建项目基础

文章目录

  • 技术栈介绍
  • 1.包管理工具pnpm的安装
  • 2.调整项目目录
  • 3.VueRouter4路由语法规则
  • [4.引入 element-ui 组件库](#4.引入 element-ui 组件库)
  • [5.Pinia - 构建用户仓库 和 持久化](#5.Pinia - 构建用户仓库 和 持久化)
    • [5.1 基本配置-构建用户仓库和开启持久化](#5.1 基本配置-构建用户仓库和开启持久化)
    • [5.2 优化-pinia 独立维护和仓库统一导出](#5.2 优化-pinia 独立维护和仓库统一导出)
      • [5.2.1 pinia 独立维护](#5.2.1 pinia 独立维护)
      • [5.2.2 仓库统一导出](#5.2.2 仓库统一导出)
  • [6. 数据交互-请求工具设计](#6. 数据交互-请求工具设计)

技术栈介绍

本项目的技术栈 本项目技术栈基于 ES6vue3piniavue-router 、vite 、axios 和 element-plus

1.包管理工具pnpm的安装

pnpm优势:比同类工具快 2倍 左右、节省磁盘空间

安装方式:mac 系统要加 sudo 提升为管理员权限

bash 复制代码
 sudo npm install -g pnpm

安装pnpm 版本为:10.12.4

创建项目:

bash 复制代码
pnpm create vue

项目初始化完成,可执行以下命令:

javascript 复制代码
cd Vue3-big-event-admin
pnpm install
pnpm dev

2.调整项目目录

默认生成的目录结构不满足我们的开发需求,所以这里需要做一些自定义改动。主要是两个工作:删除文件和修改内容

  • 删除初始化的默认文件
  • 修改剩余代码内容
  • 新增调整我们需要的目录结构
  • 拷贝初始化资源文件,安装预处理器插件

router/index.js

删掉stores 中的 js 文件

删掉views 中的.vue 文件

将 assets 文件夹中的内容删除

将main.js 中的导入 css 删除
将 app.vue 清

javascript 复制代码
<script setup>
</script>

<template>
 <div>我是 App.vue</div>
</template>

<style scoped>

</style>

新建文件夹 api 和 utils
将下面的静态资源放入到 assets 文件夹中

将 main.scss 导入进 main.js 中,需要安装scss 预处理

javascript 复制代码
pnpm add sass -D

下完之后导入进去

javascript 复制代码
import '@/assets/main.scss'

3.VueRouter4路由语法规则

默认代码如下:

javascript 复制代码
import { createRouter, createWebHistory } from 'vue-router'

//createRouter创建路由实例
//配置 history 模式: createWebHistory  地址栏不带#
//配置 hash 模式:     createHashHistory 地址栏带#
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: []
})

export default router

在 app.vue 写两个按钮模拟路由跳转

$router.push('')和 Vue2 一样

但是如果点击事件是自定义的函数,就和 Vue2 的不同了,在 Vue3 CompositionAPI 中

  1. 获取路由对象 const router=useRouter()
  2. //获取路由参数 const route =useRoute()


代码如下:

javascript 复制代码
<script setup>
import {useRoute,useRouter} from 'vue-router'
//Vue3 中 获取路由对象 router
const router=useRouter()
//获取路由参数 route
const route =useRoute()

const goList=()=>{
  console.log(router,route)
  router.push('list')
}
</script>

<template>
 <div>
  我是 App.vue
  <button @click="$router.push('/home')">跳首页</button>
  <button @click="goList">跳列表页</button>
 </div>
</template>

<style scoped>

</style>

在路由配置中的这一项:history: createWebHistory(import.meta.env.BASE_URL),

把import.meta.env.BASE_URL改成/TB,效果如下:

路径前面会多 TB

import.meta.env.BASE_URL是一个参数,在vite.config.js中修改

4.引入 element-ui 组件库

官方文档: https://element-plus.org/zh-CN/

按照官方文档:

安装:

javascript 复制代码
pnpm install element-plus

快速开始按需引入:

javascript 复制代码
pnpm add -D unplugin-vue-components unplugin-auto-import

对照文档中给出的内容进行比对配置:

配置完成后重启项目

javascript 复制代码
pnpm dev

把 button 改成 el-button 测试效果,补充:默认下 components 下的文件也会被自动注册

javascript 复制代码
<el-button @click="$router.push('/home')">跳首页</el-button >
<el-button  @click="goList">跳列表页</el-button >

5.Pinia - 构建用户仓库 和 持久化

5.1 基本配置-构建用户仓库和开启持久化

在创建 Vue 脚手架时,已经导入好了 pinia,现在开启 pinia 的持久化,完成持久化配置

官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/

安装包依赖:

javascript 复制代码
pnpm add pinia-plugin-persistedstate

将插件添加到你的 pinia 实例中

配置 stores/user.js

javascript 复制代码
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useStore = defineStore(
  'big-user',
  () => {
    const token = ref('') // 定义 token
    const setToken = (newToken) => (token.value = newToken) // 设置 token
    const removeToken=()=>{token.value=''}
    return { token,setToken,removeToken }
  },
  {
    persist: true,
  },
)

在 app.vue 中测试 pinia

javascript 复制代码
//导入 pinia
import {useUserStore} from '@/stores/user'
const userStore=useUserStore()



<p>{{ userStore.token }}</p>
<el-button @click="userStore.setToken('qrafdsgsdfs')">登入</el-button >
<el-button @click="userStore.removeToken">退出</el-button >

5.2 优化-pinia 独立维护和仓库统一导出

5.2.1 pinia 独立维护

  • 现在:初始化代码在 main.js 中,仓库代码在 stores 中,代码分散职能不单一

  • 优化:由 stores 统一维护,在 stores/index.js 中完成 pinia 初始化,交付 main.js 使用

在 main.js 中拿走和 pinia 有关的代码放到 stores 下的 index.js

移除前的 main.js

移除后的 main.js和移除后的 index.js

重新启动服务

javascript 复制代码
pnpm dev

5.2.2 仓库统一导出

  • 现在:使用一个仓库 import { useUserStore } from ./stores/user.js 不同仓库路径不一致

  • 优化:由 stores/index.js 统一导出,导入路径统一 ./stores,而且仓库维护在 stores/modules 中

随着项目的扩大,将 stores 下面的 js 内容放到了 modules文件夹内

如果想在 app.vue 中导入就要写

javascript 复制代码
import {useUserStore} from '@/stores/modules/user'

这样太麻烦了,希望改成如下情况,有 stores 下面的 index.js 统一管理

javascript 复制代码
import {useUserStore,XXXStore} from '@/stores'

在改写 index.js

javascript 复制代码
import{useUserStore} from './modules/user'
export{useUserStore} 

//假设还有一个 store
import{useCountStore} from './modules/user'
export{useCountStore} 

这样写还是麻烦,改为更简单的方法

javascript 复制代码
export * from './modules/user'
export * from './modules/counter'

6. 数据交互-请求工具设计

一开始做项目的时候,确实可以直接用 axios 请求接口数据,不需要配置什么拦截器

但随着项目越来越复杂,你就会发现这样写代码有一堆问题

写在utils/request.js

首先按照 axios

javascript 复制代码
pnpm add axios
javascript 复制代码
1.每个请求都要手动加 token,很麻烦
axios.get('/api/userinfo', {
  headers: {
    Authorization: store.token
  }
})
你有 20 个接口,就要写 20 次 headers,改 token 也要改 20 次。

2.错误提示要手动写,容易忘
axios.get('/api/userinfo')
  .then(res => {
    if (res.data.code !== 0) {
      ElMessage.error(res.data.message)
    }
  })
 你会发现:所有接口都要加 if...else,非常重复,而且不统一,UI 上也不好看。
3.token 过期时跳登录,没法统一处理
.catch(err => {
  if (err.response.status === 401) {
    router.push('/login')
  }
})
太繁琐

有了拦截器,一切就变得自动化了!
// 请求拦截器:自动加 token
// 响应拦截器:自动弹提示 + 跳登录
你只需要这样用就行了:
request.get('/my/userinfo')
无需关注 token、错误码、跳转页面,所有的逻辑已经帮你封装好了。

统一封装 axios 请求模板:

官方文档:http://www.axios-js.com/zh-cn/docs/#axios-create-config

javascript 复制代码
import axios from 'axios'

const baseURL = 'http://big-event-vue-api-t.itheima.net'

const instance = axios.create({
  // 1. 基础地址,超时时间(配置项)
})

//请求拦截器
instance.interceptors.request.use(
  (config) => {
    // 2. 携带token,将 token 值赋给请求头
    return config
  },
  (err) => Promise.reject(err)
)

//响应拦截器
instance.interceptors.response.use(
//http 返回 200 成功
  (res) => {  
    // 3. 正确拿到了业务数据
    
    // 4. 业务逻辑错误,例如限制密码字母大写
    return res
  },
  (err) => {
    // 5. 处理http 返回401错误
    return Promise.reject(err)
  }
)

export default instance

本项目封装:

javascript 复制代码
//导入 stores axios router 和组件库 element-ui
import { useUserStore } from '@/stores/user'
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'

const baseURL = 'http://big-event-vue-api-t.itheima.net'

//创建 axios 实例,配置一些默认项,如超时 10 秒
const instance = axios.create({
  baseURL,
  timeout: 100000
})

//请求拦截器
instance.interceptors.request.use(
//http200 返回正确
  (config) => {
    const userStore = useUserStore()
    //如果有 token,将 token赋值给请求头,为什么是Authorization,见解释 1
    if (userStore.token) { 
      config.headers.Authorization = userStore.token
    }
    return config
  },
  //错误抛出错误信息
  (err) => Promise.reject(err)
)

instance.interceptors.response.use(
  (res) => {
      //业务逻辑正确(code=0),处理如下
    if (res.data.code === 0) {
      return res
    }
    //业务逻辑错误(code!=0),手动抛出错误信息,ElMessage是组件,type是他的样式
    ElMessage({ message: res.data.message || '服务异常', type: 'error' })
    return Promise.reject(res.data)
  },
  //如果 http 返回是是 401,表示用户未登入,或者 token 过期,得重新登入
  (err) => {
    ElMessage({ message: err.response.data.message || '服务异常', type: 'error' })
    console.log(err)
    if (err.response?.status === 401) {
      router.push('/login')
    }
    //其他错误抛出错误信息
    return Promise.reject(err)
  }
)
//instance(默认导出)	是你真正用来发请求的 axios 实例
//baseURL 导出整个请求的"基础路径前缀"
export default instance
export { baseURL }

解释 1:为什么是Authorization?

看接口文档,给你的请求头需要什么?

解释 2:为什么在响应拦截器中 res 中写的返回是 return Promise.reject(res.data),err 写的是这个return Promise.reject(err)?

拦截器分支 拿到的内容 我们关心什么 为什么这么写
res => {} HTTP 200 成功返回 业务数据的状态码res.data.code 所以 Promise.reject(res.data),把"业务失败信息"抛出去
err => {} 网络错误 / 状态码非 200 完整错误对象(包含状态码、错误信息) 所以 Promise.reject(err),保留完整异常信息给调用方分析

res 关注的是 code=几,表示是哪里自定义的错误的业务逻辑

解释 3:导出使用

javascript 复制代码
// 使用时
import request, { baseURL } from '@/utils/request'
相关推荐
aesthetician3 小时前
ahooks:一套高质量、可靠的 React Hooks 库
前端·react.js·前端框架
shizhenshide3 小时前
如何在同一站点支持多版本的 reCAPTCHA 的兼容性方案
服务器·前端·网络·安全·captcha·ezcaptcha
CodeCraft Studio3 小时前
借助Aspose.HTML控件,使用 Python 编程创建 HTML 页面
前端·python·html·aspose·python创建html·html sdk
杨超越luckly3 小时前
HTML应用指南:利用GET请求获取全国奥迪授权经销商门店位置信息
大数据·前端·python·html·数据可视化·门店数据
05Nuyoah3 小时前
DAY 01 HTML的认识
前端·html
嚣张农民4 小时前
还在自己买服务器?试试 Amazon EC2,真香!
前端·后端·程序员
charlie1145141914 小时前
Chrome View渲染机制学习小记
前端·chrome·学习·渲染·gpu·客户端
Dontla4 小时前
Yarn命令与npm命令的区别与联系(npm:Node.js的官方包管理工具;Yarn:Facebook开发的JavaScript包管理工具)
javascript·npm·node.js
多看书少吃饭4 小时前
vue3+TS 前端调用海康摄像头视频流,后端用 Node.js 做 RTSP 转 WebSocket-FLV 转发,并且前后端优化延迟方案
前端·websocket·node.js