
Vue3 + Vite 中级编程总结三
2025年1月10日...大约 16 分钟
Vue3 + Vite 中级编程总结三
电商项目开发环境搭建(硅谷甄选平台)
开发环境版本
node v16.14.2
pnpm v8.0.0
安装 pnmp 包管理工具
npm install pnpm@8.0 -g
创建项目
pnpm crate vite
运行项目
# 进入项目所在目录
cd project
# 安装项目初始化依赖包
pnpm install
# package.json中添加的包如下:
dependencies:
+ vue 3.2.47
devDependencies:
+ @vitejs/plugin-vue ^4.1.0
+ @vue/tsconfig 0.7.0
+ typescript ^4.9.3
+ vite ^4.2.0
+ vue-tsc ^1.2.0
# 启动服务
pnpm run dev
# 浏览器运行http://127.0.0.1:5175/可以看到一个初始页面
Local: http://127.0.0.1:5175/
安装 eslint 代码检查工具
pnpm install eslint@^8.38.0 -D
# 项目根目录下创建.eslint.cjs 配置文件
npx eslint --init
# 安装vue3环境代码校验插件
pnpm install -D eslint-plugin-import@^2.27.5 eslint-plugin-vue@^9.10.0 eslint-plugin-node@^11.1.0 eslint-plugin-prettier@^4.2.1 eslint-config-prettier@^8.8.0 eslint-plugin-node@^11.1.0 @babel/eslint-parser@^7.21.3
# 插件解释
# 让所有与prettier规则存在冲突的Eslint rules失效,并使用prettier进行代码检查
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
# 运行更漂亮的Eslint,使prettier规则优先级更高,Eslint优先级低
"eslint-plugin-prettier": "^4.2.1",
# vue.js的Eslint插件(查找vue语法错误,发现错误指令,查找违规风格指南
"eslint-plugin-vue": "^9.10.0",
# 该解析器允许使用Eslint校验所有babel code
"@babel/eslint-parser": "^7.21.3",
# 修改.eslintrc.cjs配置文件
// @see https://eslint.bootcss.com/docs/rules/
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
jest: true,
},
/* 指定如何解析语法 */
parser: 'vue-eslint-parser',
/** 优先级低于 parse 的语法解析配置 */
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
/* 继承已有的规则 */
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['vue', '@typescript-eslint'],
/*
* "off" 或 0 ==> 关闭规则
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
*/
rules: {
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的转义字符
// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
'@typescript-eslint/semi': 'off',
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
},
}
# 项目根目录下创建 .eslintignore 配置忽略文件
dist
node_modules
# # 运行脚本添加两个脚本命令
# 运行这两个命令可以检查src目录下的文件是否符合规则以及执行尽可能的修复操作
"scripts": {
"lint": "eslint src",
"fix": "eslint src --fix",
}
安装 prettier 及其依赖包
pnpm install -D eslint-plugin-prettier@^4.2.1 prettier@^4.2.1 eslint-config-prettier@^8.8.0
# 项目根目录下创建 .prettierrc.json 配置文件。
# eslint和prettier这俩兄弟一个保证js代码质量,一个保证代码美观。
{
"singleQuote": true, 字符串要求是单引号
"semi": false, 不要分号
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2 缩进占两个字符位置
}
# 项目根目录下创建.prettierignore 配置忽略文件
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
# 通过 pnpm run lint 检测语法,如果出现不规范格式,通过 pnpm run fix 修改错误
pnpm run lint # 检测语法
pnpm run fix # 修改错误
安装 stylelint css 检查工具及其依赖包
pnpm add sass@^1.61.0 sass-loader@^13.2.2 stylelint@15.4.0 postcss@15.4.0 postcss-scss@15.4.0 postcss-html@^4.0.6 stylelint-config-prettier@^4.0.6 stylelint-config-recess-order@^4.0.6 stylelint-config-recommended-scss@^9.0.1 stylelint-config-standard@^32.0.0 stylelint-config-standard-vue@^1.0.0 stylelint-scss@^4.6.0 stylelint-order@^6.0.3 stylelint-config-standard-scss@^7.0.1 -D
# 项目根目录下创建.stylelintrc.cjs配置文件
// @see https://stylelint.bootcss.com/
module.exports = {
extends: [
'stylelint-config-standard', // 配置stylelint拓展插件
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
'stylelint-config-standard-scss', // 配置stylelint scss插件
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
'stylelint-config-prettier', // 配置stylelint和prettier兼容
],
overrides: [
{
files: ['**/*.(scss|css|vue|html)'],
customSyntax: 'postcss-scss',
},
{
files: ['**/*.(html|vue)'],
customSyntax: 'postcss-html',
},
],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
'**/*.yaml',
],
/**
* null => 关闭该规则
* always => 必须
*/
rules: {
'value-keyword-case': null, // 在 css 中使用 v-bind,不报错
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
'no-empty-source': null, // 关闭禁止空源码
'selector-class-pattern': null, // 关闭强制选择器类名的格式
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符
'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box
'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask
'selector-pseudo-class-no-unknown': [
// 不允许未知的选择器
true,
{
ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到
},
],
},
}
# 项目根目录下创建.stylelintignore 配置忽略文件
/node_modules/*
/dist/*
/html/*
/public/*
# 运行脚本添加一条命令
"scripts": {
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
}
# 最后配置统一的prettier来格式化我们的js和css,html代码。
"scripts": {
"dev": "vite --open",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src",
"fix": "eslint src --fix",
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
},
# 当我们运行 pnpm run format 的时候,会把代码直接格式化。
安装 husky git 提交代码格式化工具
pnpm install -D husky@^8.0.0
# 执行 npx husky-init 生成 .husky目录。
npx husky-init
# 修改.husky目录下面的pre-commit文件
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run format
# 当我们对代码进行commit操作的时候,就会执行命令,对代码进行格式化,然后再提交。
在项目根目录下创建 commitlint.config.cjs 配置文件
module.exports = {
extends: ['@commitlint/config-conventional'],
// 校验规则
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'chore',
'revert',
'build',
],
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72],
},
}
# 安装包文件
pnpm add @commitlint/config-conventional@^17.4.4 @commitlint/cli@^17.5.1 -D
# 以后commit的时候就到带上下面的信息,否则提交通不过。
'feat', //新特性、新功能
'fix', //修改bug
'docs', //文档修改
'style', //代码格式修改, 注意不是 css 修改
'refactor', //代码重构
'perf', //优化相关,比如提升性能、体验
'test', //测试用例修改
'chore', //其他修改, 比如改变构建流程、或者增加依赖库、工具等
'revert', //回滚到上一个版本
'build', //编译相关的修改,例如发布版本、对项目构建或者依赖的改动
# 配置husky
npx husky add .husky/commit-msg
# 在生成的commit-msg中添加如下信息
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm commitlint
#当我们 commit 提交信息时,就不能再随意写了,必须是 git commit -m 'fix: xxx' 符合类型的才可以,需要注意的是类型的后面需要用英文的 :,并且冒号后面是需要空一格的,这个是不能省略的。
强制使用 pnpm 包管理工具
# 在根目录创建scritps/preinstall.js文件
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`\u001b[33mThis repository must using pnpm as the package manager ` +
` for scripts to work properly.\u001b[39m\n`,
)
process.exit(1)
}
# 配置命令
"scripts": {
"preinstall": "node ./scripts/preinstall.js"
}
# 当我们使用npm或者yarn来安装包的时候,就会报错了。原理就是在install的时候会触发preinstall(npm提供的生命周期钩子)这个文件里面的代码。
项目集成
安装 element-plus
pnpm i element-plus@^2.3.3 @element-plus/icons-vue@^2.1.0
导入 element-plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//@ts-ignore忽略当前文件ts类型的检测否则有红色提示(打包会失败)
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
app.use(ElementPlus, {
locale: zhCn,
})
// 关键:将 PascalCase 转换为 el-icon-短横线格式,全局注册图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
const kebabName = key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() // 例如 AddLocation → add-location
app.component(`el-icon-${kebabName}`, component) // 注册为 <el-icon-add-location>
}
// 模板里引用图标。下面是显示单独图标和在按钮中显示图标的写法
<el-icon-alarm-clock style="width: 1em; height: 1em; color: #40990eff" />
<el-button type="success">
<el-icon-edit style="width: 1em; height: 1em; color: #409eff" />
保存
</el-button>
// 在单个文件中引入需要的图标组件,写法如下。
// 模板中引用写法:icon="Plus"
<script setup lang="ts">
import { Plus, Check } from '@element-plus/icons-vue'
</script>
tsconfig.json 添加如下内容,配置完毕可以测试 element-plus 组件与图标的使用。
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
src 别名配置
在开发项目的时候文件与文件关系可能很复杂,因此我们需要给 src 文件夹配置一个别名!
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve("./src"), // 相对路径别名配置,使用 @ 代替 src
},
},
});
环境变量配置
开发环境 、测试环境、生产环境。一般每种环境对应一台服务器。在项目的根目录下创建以下几个配置文件。即.env.development | .env.production | .env.test 三个配置文件。通过 import.meta.env 获取环境变量。
# .env.development
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/dev-api'
# .env.production
NODE_ENV = 'production'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/prod-api'
# .env.test
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'test'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/test-api'
配置运行命令
"scripts": {
"dev": "vite --open",
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode production",
"preview": "vite preview"
},
至此全部 package.json 文件如下
{
"name": "project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --open",
"build": "vue-tsc && vite build",
"preview": "vite preview --open",
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode production",
"lint": "eslint src",
"fix": "eslint src --fix",
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix",
"prepare": "husky install",
"commitlint": "commitlint --config commitlint.config.cjs -e -V",
"preinstall": "node ./scripts/preinstall.js"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"axios": "^1.3.5",
"echarts": "^5.4.2",
"echarts-liquidfill": "^3.1.0",
"lodash": "^4.17.21",
"element-plus": "^2.3.3",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"pinia": "^2.0.34",
"progress": "^2.0.3",
"vue": "^3.2.47",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@babel/eslint-parser": "^7.21.3",
"@commitlint/cli": "^17.5.1",
"@commitlint/config-conventional": "^17.4.4",
"@types/nprogress": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-vue": "^4.1.0",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.10.0",
"husky": "^8.0.0",
"mockjs": "^1.1.0",
"postcss": "^8.4.21",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
"prettier": "^2.8.7",
"sass": "^1.61.0",
"sass-loader": "^13.2.2",
"stylelint": "^15.4.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-recess-order": "^4.0.0",
"stylelint-config-recommended-scss": "^9.0.1",
"stylelint-config-standard": "^32.0.0",
"stylelint-config-standard-scss": "^7.0.1",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-order": "^6.0.3",
"stylelint-scss": "^4.6.0",
"typescript": "^4.9.3",
"vite": "^4.2.0",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^1.2.0"
}
}
SVG 图标集成
在开发项目的时候经常会用到 svg 矢量图,而且我们使用 SVG 以后,页面上加载的不再是图片资源,这对页面性能来说是个很大的提升,而且我们 SVG 文件比 img 要小的很多,放在项目中几乎不占用资源。需要在
# 安装SVG依赖插件
pnpm install vite-plugin-svg-icons@^2.0.1 -D
// vite.config.ts 文件中添加代码
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";
export default () => {
return {
plugins: [
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// Specify symbolId format
symbolId: "icon-[dir]-[name]",
}),
],
};
};
至此 vite.config.ts 文件如下:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
// 引入svg插件
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// Specify symbolId format
symbolId: "icon-[dir]-[name]",
}),
],
resolve: {
alias: {
"@": path.resolve("./src"), // 相对路径别名配置,使用 @ 代替 src
},
},
});
// 入口文件main.js导入下面语句
import "virtual:svg-icons-register";
<!-- Test.vue -->
<!-- svg图标的外层容器,内部需要与use标签结合使用 -->
<svg width="24" height="24">
<use href="#icon-mysvg"></use>
</svg>
封装一个 SvgIcon 全局组件
<!--src/components/SvgIcon/index.vue -->
<template>
<div>
<svg :style="{ width: width, height: height }">
<use :xlink:href="prefix + name" :fill="color"></use>
</svg>
</div>
</template>
<script setup lang="ts">
defineProps({
//xlink:href属性值的前缀
prefix: {
type: String,
default: "#icon-",
},
//svg矢量图的名字
name: String,
//svg图标的颜色
color: {
type: String,
default: "",
},
//svg宽度
width: {
type: String,
default: "16px",
},
//svg高度
height: {
type: String,
default: "16px",
},
});
</script>
<style scoped></style>
在 src/components 文件夹目录下创建一个 index.ts 文件:用于注册 components 文件夹内部全部全局组件。
import SvgIcon from "./SvgIcon/index.vue";
import type { App, Component } from "vue";
const components: { [name: string]: Component } = { SvgIcon };
export default {
install(app: App) {
Object.keys(components).forEach((key: string) => {
app.component(key, components[key]);
});
},
};
// 引入 src/components/index.ts 文件,通过 app.use 方法安装自定义插件
import gloablComponent from "./components/index.js";
// ... ...
app.use(gloablComponent);
集成 Sass
<!-- 需要添加lang属性指定为scss -->
<style scoped lang="scss"></style>
在 src/styles 目录下创建一个 index.scss 文件和 variable.scss 文件。在 npm 网站上下载 reset.scss 文件。在 index.scss 中引入 reset.scss 以便清除默认样式。
// index.scss
@import reset.scss // 在入口文件引入
import "@/styles/index.scss" // variable.scss文件定义的全局变量如下
//左侧的菜单的宽度
$base-menu-width: 260px;
//左侧菜单的背景颜色
$base-menu-background: #001529;
$base-menu-min-width: 50px;
// 顶部导航的高度
$base-tabbar-height: 50px;
//左侧菜单logo高度设置
$base-menu-logo-height: 50px;
//左侧菜单logo右侧文字大小
$base-logo-title-fontSize: 20px;
//我的颜 色测试
$h1-color: #ff0808;
在 vue 样式部分使用 variable.scss 中定义好的全局变量。
<style scoped lang="scss">
div {
h1 {
color: $h1-color;
}
}
.svg {
width: 100%;
height: 300px;
background-color: $base-menu-background;
}
</style>
mock 数据
# 安装mock及其依赖
pnpm install -D vite-plugin-mock@^2.9.6 mockjs@^1.1.0
在 vite.config.js 配置文件启用插件。
import { defineConfig } from "vite";
import { viteMockServe } from "vite-plugin-mock";
import vue from "@vitejs/plugin-vue";
import path from "path";
// 引入svg插件
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
// https://vite.dev/config/
export default defineConfig(({ command }) => {
return {
plugins: [
vue(),
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// Specify symbolId format
symbolId: "icon-[dir]-[name]",
}),
viteMockServe({
localEnabled: command === "serve", //保证开发环境可以使用mock数据
}),
],
resolve: {
alias: {
"@": path.resolve("./src"), // 相对路径别名配置,使用 @ 代替 src
},
},
//scss全局变量一个配置
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";',
},
},
},
};
});
在项目的根目录下创建一个 mock 文件夹,在 mock 文件夹下创建一个 user.ts 文件
//用户信息数据 user.ts
function createUserList() {
return [
{
userId: 1,
avatar:
"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
username: "admin",
password: "111111",
desc: "平台管理员",
roles: ["平台管理员"],
buttons: ["cuser.detail"],
routes: ["home"],
token: "Admin Token",
},
{
userId: 2,
avatar:
"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
username: "system",
password: "111111",
desc: "系统管理员",
roles: ["系统管理员"],
buttons: ["cuser.detail", "cuser.user"],
routes: ["home"],
token: "System Token",
},
];
}
export default [
// 用户登录接口
{
url: "/api/user/login", //请求地址
method: "post", //请求方式
response: ({ body }) => {
//获取请求体携带过来的用户名与密码
const { username, password } = body;
//调用获取用户信息函数,用于判断是否有此用户
const checkUser = createUserList().find(
(item) => item.username === username && item.password === password
);
//没有用户返回失败信息
if (!checkUser) {
return { code: 201, data: { message: "账号或者密码不正确" } };
}
//如果有返回成功信息
const { token } = checkUser;
return { code: 200, data: { token } };
},
},
// 获取用户信息
{
url: "/api/user/info",
method: "get",
response: (request) => {
//获取请求头携带token
const token = request.headers.token;
//查看用户信息是否包含有次token用户
const checkUser = createUserList().find((item) => item.token === token);
//没有返回失败的信息
if (!checkUser) {
return { code: 201, data: { message: "获取用户信息失败" } };
}
//如果有返回成功信息
return { code: 200, data: { checkUser } };
},
},
];
最后安装 axios
pnpm install axios@^1.3.5
模拟接口请求并返回数据。
//测试mock接口能否使用
import axios from 'axios'
//登陆接口
axios({
url: '/api/user/login',
method: 'post',
data: {
username: 'admin',
password: '111111',
},
})
//请求结果:浏览器调试工具打开网络,点击login接口,响应部显示如下:
{"code":200,"data":{"token":"Admin Token"}}
axios 二次封装,可以使用请求响应拦截器
// src/utils/request.ts
import axios from "axios";
import { ElMessage } from "element-plus";
//创建axios实例
let request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000,
});
//请求拦截器
request.interceptors.request.use((config) => {
return config;
});
//响应拦截器
request.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
//处理网络错误
let msg = "";
let status = error.response.status;
switch (status) {
case 401:
msg = "token过期";
break;
case 403:
msg = "无权访问";
break;
case 404:
msg = "请求地址错误";
break;
case 500:
msg = "服务器出现问题";
break;
default:
msg = "无网络";
}
ElMessage({
type: "error",
message: msg,
});
return Promise.reject(error);
}
);
export default request;
API 接口统一管理
// ==================src/api/user/index.ts==================
// 统一管理项目用户相关的接口
import request from "@/utils/request";
import type { loginForm, loginResposeData, userResponseData } from "./type";
// 统一管理接口
enum API {
LOGIN_URL = "/user/login",
USERINFO_URL = "/user/info",
}
// 暴露请求函数
// 登陆接口方法
export const reqLogin = (data: loginForm) =>
request.post<any, loginResposeData>(API.LOGIN_URL, data);
// 获取用户信息的方法
export const reqUserInfo = () =>
request.get<any, userResponseData>(API.USERINFO_URL);
// ==================src/api/user/type.ts==================
// 登陆接口需要携带参数类型
export interface loginForm {
username: string;
password: string;
}
interface dataType {
token: string;
}
// 登陆接口返回数据类型
export interface loginResposeData {
code: number;
data: dataType;
}
// 用户信息类
interface userInfo {
userId: number;
avatar: string;
username: string;
password: string;
desc: string;
roles: string[];
buttons: string[];
routes: string[];
token: string;
}
//用户类
interface user {
checkUser: userInfo;
}
// 定义服务器返回用户信息相关的数据类型
export interface userResponseData {
code: number;
data: user;
}
项目开发
路由配置
pnpm install vue-router@^4.1.6
配置仓库
pnpm install pinina@^2.0.34
// src/store/index.ts
// 创建大仓库
import { createPinia } from 'pinia'
// 创建大仓库
const pinia = createPinia()
export default pinia
// src/main.ts
// 注册全局仓库
import pinia from './store/index.js'
app.use(pinia)
// src/store/modules/user.ts
// 创建用户相关的小仓库
import { loginForm } from '@/api/user/type'
import { defineStore } from 'pinia'
import { reqLogin } from '@/api/user'
//创建用户小仓库
const useUserStore = defineStore('user', {
state: () => {
return {
token: localStorage.getItem('TOKEN'),
}
},
actions: {
async userLogin(loginForm: loginForm) {
// 登录请求
const result: any = await reqLogin(loginForm)
console.log(result)
// 登录成功,获取到token ,成功码200,登录成功需要把token存储起来。
if (result.code == 200) {
// pinina仓库储存token
// 由于pinina储存数据并非持久化
this.token = result.data.token
// 本地持久化存储一份数据
localStorage.setItem('TOKEN', result.data.token)
//这句话能保证async函数返回一个成功的promise
return 'ok'
} else {
return Promise.reject(new Error(result.data.message))
}
// 登录请求失败 ,失败码201
},
},
getters: {},
})
//对外暴露获取小仓库方法
export default useUserStore