Vite创建vue3+ts+pinia+vant项目起步流程

news2024/11/18 3:43:40

pnpm介绍&安装

本质上他是一个包管理工具,和npm/yarn没有区别,主要优势在于

  • 包安装速度极快
  • 磁盘空间利用效率高

安装:

npm i pnpm -g

使用:

npm命令pnpm等效
npm installpnpm install
npm i axiospnpm add axios
npm i webpack -Dpnpm add webpack -D
npm run devpnpm dev

小结:

  • pnpm 是一个高效的包管理工具,使用和npm和yarn基本相同

1、项目创建

使用 create-vue 脚手架创建项目

create-vue,即官方的项目脚手架工具,提供了搭建基于 Vite且 TypeScript 就绪的 Vue 项目的选项。

步骤:

  1. 执行创建命令
pnpm create vue
# or
npm init vue@latest
# or
yarn create vue
  1. 选择项目依赖内容
✔ Project name: … patients-h5-100
✔ Add TypeScript? … No / `Yes`
✔ Add JSX Support? … `No` / Yes
✔ Add Vue Router for Single Page Application development? … No / `Yes`
✔ Add Pinia for state management? … No / `Yes`
✔ Add Vitest for Unit Testing? … `No` / Yes
✔ Add Cypress for both Unit and End-to-End testing? … `No` / Yes
✔ Add ESLint for code quality? … No / `Yes`
✔ Add Prettier for code formatting? … No / `Yes`

Scaffolding project in /Users/zhousg/Desktop/patient-h5-100...

Done. Now run:

  cd patient-h5-100
  pnpm install
  pnpm lint
  pnpm dev

2、项目准备工作

vscode插件安装

安装:项目开发需要的一些插件

必装:

  • Vue Language Features (Volar) 语法高亮,代码提示, volar支持setup语法糖,官方推荐vue3使用
  • TypeScript Vue Plugin (Volar) vvue3中更好的ts提示, 让Ts服务知道.vue文件
  • Eslint 代码风格校验

注意

  • vscode 安装了 Prettier 插件的可以先 禁用,或者关闭保存自动格式化功能,避免和项目的 Eslint 风格冲突。
  • (Volar为vue3开发)(vetur主要是vue2)Volar和vetur不能共存

可选:

  • gitLens 代码git提交记录提示
  • json2ts json自动转ts类型
  • Error Lens 行内错误提示 💡

提示:

大中型项目建议开启 TS托管模式 , 更好更快的类型提示。

更高效的开启TS托管模式 (Take Over Mode(托管模式),TS服务性能更好)

什么是托管模式,vscode内置了一套ts服务,volar也提供了一套ts服务,所以需要开启托管模式,让ts性能提示更加高效,只需要下面两步即可,1.关闭vscode内置的TS服务,2.使用Volar提供的TS服务

vscode 插件中搜索 @builtin ty,在当前工作区禁用ts服务后,重启vscode即可。

在这里插入图片描述

查看ts已托管成功,重启后vscode工具栏下方有这个提示即可

在这里插入图片描述

这里就相当于告诉vscode,你不需要提供ts服务了,我找个人(volar插件)来替你服务,即所谓的托管。

eslint 预制配置

.eslintrc.cjs文件
加入如下配置,覆盖原有的eslint配置

  rules: {
    'prettier/prettier': [
      'warn',
      {
        singleQuote: true,//单引号
        semi: false,//没有分号
        printWidth: 80,//行宽度80字符
        trailingComma: 'none',//没有对象数组最后一个逗号
        endOfLine: 'auto'//换行字符串自动(系统不一样换行符号不一样)
      }
    ],
    //新手在组件命名的时候不够规范,根据官方风格指南,除了根组件(App.vue)外,
    // 自定义组件名称应该由多单词组成,防止和html标签冲突。
    'vue/multi-word-component-names': [
      'warn',
      {
        ignores: ['index']
      }
    ],
    // 允许对 props 进行解构
    'vue/no-setup-props-destructure': ['off'],
    // 添加未定义变量错误提示
    'no-undef': 'error'
  }

  • 格式:单引号,没有分号,行宽度80字符,没有对象数组最后一个逗号,换行字符串自动(系统不一样换行符号不一样)
  • vue 组件需要大驼峰命名,除去 index 之外,App 是默认支持的
  • 允许对 props 进行解构,我们会开启解构保持响应式的语法糖

执行:

# 修复格式
pnpm lint

vscode 开启 eslint 自动修复

    "editor.codeActionsOnSave": {
        "source.fixAll": true,
    },

提示安装Eslint且配置保存修复,不要开启默认的自动保存格式化

代码检查工作流

提交代码前做代码检查 -使用husky这个git hooks工具(husky相当于是git的钩子)

husky [ˈhʌski] 哈士奇

eslint只能修复代码的风格,不能修复代码的错误,此时提交代码比较危险

怎么做?安装husky工具(这个工具是在commit提交文件之前检查代码是否有问题的和git一起用)

  1. 初始化git仓库,执行git init即可

  2. 初始化husky工具配置,执行

    pnpm dlx husky-init && pnpm install
    
  3. 修改 .husky/pre-commit 文件,添加pnpm lint 这个命令

    在这里插入图片描述

pnpm lint是全量检查,突然添加就会有耗时问题和检查历史遗留问题,所以最好只检查修改的文件,在暂存区做lint校验(git add .的时候把文件提交到了暂存区),在commit命令之前去执行

如何只检查暂存区代码?

  1. 安装lint-staged

    pnpm i lint-staged -D
    
  2. 配置 package.json

    {
      // ... 省略 ...
      "lint-staged": {
        "*.{js,ts,vue}": [
          "eslint --fix"
        ]
      }
    }
    
    {
      "scripts": {
        // ... 省略 ...
        "lint-staged": "lint-staged"
      }
    }
    
  3. 修改 .husky/pre-commit 文件

    pnpm lint-staged
    
  4. 图示

    在这里插入图片描述

在这里插入图片描述

项目结构调整

了解:每一个目录结构的作用

./src
    ├── assets        `静态资源,图片...`
    ├── components    `通用组件`
    ├── composables   `组合功能通用函数`
    ├── icons         `svg图标`
    ├── router        `路由`
    │   └── index.ts
    ├── services      `接口服务API`
    ├── stores        `状态仓库`
    ├── styles        `样式`
    │   └── main.scss
    ├── types         `TS类型`
    ├── utils         `工具函数`
    ├── views         `页面`
    ├── main.ts       `入口文件`
    └──App.vue       `根组件`

assets, components, stores, views 清空文件夹

composables, icons, services, styles/main.scss, types, utils 新建文件夹

router/index.ts, main.ts, App.vue 修改

router/index.ts

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

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

export default router

main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

//导入新创建的全局样式文件
import './styles/main.scss'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

App.vue

<script setup lang="ts"></script>

<template>
  <div>App</div>
</template>

<style scoped></style>

项目使用sass预处理器,安装sass,即可支持scss语法:

pnpm add sass -D

导入 main.ts 作为全局样式

import './styles/main.scss'

路由代码解析

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

// createRouter 创建路由实例,===> new VueRouter()
// history 是路由模式,hash模式,history模式
// createWebHistory() 是开启history模块   http://xxx/user
// createWebHashHistory() 是开启hash模式    http://xxx/#/user

// vite 的配置 import.meta.env.BASE_URL 是路由的基准地址,默认是 ’/‘
// https://vitejs.dev/guide/build.html#public-base-path
// 如果将来你部署的域名路径是:http://xxx/my-path/user
// vite.config.ts  添加配置  base: my-path,路由这就会加上 my-path 前缀了

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

export default router
  1. 创建路由实例由 createRouter 实现
  2. 路由模式
    • history 模式使用 createWebHistory()
    • hash 模式使用 createWebHashHistory()
    • 参数是基础路径,默认/

扩展:import.meta.env.BASE_URL

import.meta 是 JavaScript 模块暴露的描述模块的信息对象

在这里插入图片描述

env.BASE_URL 是Vite 环境变量

在这里插入图片描述

3、移动端项目基础架构要做的工作

在这里插入图片描述

4、构建界面

vant组件库

文档

安装:

# Vue 3 项目,安装最新版 Vant
npm i vant
# 通过 yarn 安装
yarn add vant
# 通过 pnpm 安装
pnpm add vant

引入组件 - vant 自动引入组件,并按需引入组件的样式:

在基于 vitewebpackvue-cli 的项目中使用 Vant 时,可以使用 unplugin-vue-components 插件,它可以自动引入组件。

Vant 官方基于 unplugin-vue-components 提供了自动导入样式的解析器 @vant/auto-import-resolver,两者可以配合使用。

  1. 安装插件

    # 通过 npm 安装
    npm i @vant/auto-import-resolver unplugin-vue-components -D
    
    # 通过 yarn 安装
    yarn add @vant/auto-import-resolver unplugin-vue-components -D
    
    # 通过 pnpm 安装
    pnpm add @vant/auto-import-resolver unplugin-vue-components -D
    
    # 通过 bun 安装
    bun add @vant/auto-import-resolver unplugin-vue-components -D
    
  2. 配置插件

    如果是基于 vite 的项目,在 vite.config.js 文件中配置插件:

    import vue from '@vitejs/plugin-vue';
    import Components from 'unplugin-vue-components/vite';
    import { VantResolver } from '@vant/auto-import-resolver';
    
    export default {
      plugins: [
        vue(),
        Components({
          resolvers: [VantResolver()],
        }),
      ],
    };
    
  3. 使用组件

    <template>
      <van-button type="primary" />
    </template>
    
  4. 引入函数组件的样式

    Vant 中有个别组件是以函数的形式提供的,包括 ToastDialogNotifyImagePreview 组件。在使用函数组件时,unplugin-vue-components 无法解析自动注册组件,导致 @vant/auto-import-resolver 无法解析样式,因此需要手动引入样式。

    // Toast
    import { showToast } from 'vant';
    import 'vant/es/toast/style';
    
    // Dialog
    import { showDialog } from 'vant';
    import 'vant/es/dialog/style';
    
    // Notify
    import { showNotify } from 'vant';
    import 'vant/es/notify/style';
    
    // ImagePreview
    import { showImagePreview } from 'vant';
    import 'vant/es/image-preview/style';
    

移动端适配

实现:使用 vw 完成移动端适配

文档

安装:

npm install postcss-px-to-viewport -D
# or
yarn add -D postcss-px-to-viewport
# or
pnpm add -D postcss-px-to-viewport

配置: postcss.config.js

// eslint-disable-next-line no-undef
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      // 设备宽度375计算vw的值
      viewportWidth: 375,
    },
  },
};

测试:

在这里插入图片描述

  • 有一个控制台警告可忽略,或者使用 postcss-px-to-viewport-8-plugin 代替当前插件

css变量主题定制

实现:使用css变量定制项目主题,和修改vant主题

官方文档

  • 全局变量:使用场景:项目主题

    :root {
       /* 全局css变量 */
      --main: #999;
      --cp-primary: #16C2A3;
       /* 覆盖vant主体色 */
      --van-primary-color: var(--cp-primary);
    }
    
    a {
      /* 使用变量 */
      color: var(--main)
    }
    
  • 局部变量:使用场景:组件变量

    .footer {
      /* 局部变量 */
      --footer-color: green;
    }
     
    .footer {
     /* 只能在footer下使用 */
      color: var(--footer-color);
    }
    
  • 如何覆盖vant的主题色:找到主题色的变量名,覆盖即可

    styles/main.scss
    注意:为什么要写两个重复的 :root?
    由于 vant 中的主题变量也是在 :root 下声明的,所以在有些情况下会由于优先级的问题无法成功覆盖。通过 :root:root 可以显式地让你所写内容的优先级更高一些,从而确保主题变量的成功覆盖。

    :root:root {
      // 问诊患者:色板
      --cp-primary: #16C2A3;
      --cp-plain: #EAF8F6;
      --cp-orange: #FCA21C;
      --cp-text1: #121826;
      --cp-text2: #3C3E42;
      --cp-text3: #6F6F6F;
      --cp-tag: #848484;
      --cp-dark: #979797;
      --cp-tip: #C3C3C5;
      --cp-disable: #D9DBDE;
      --cp-line: #EDEDED;
      --cp-bg: #F6F7F9;
      --cp-price: #EB5757;
      // 覆盖vant主题色
      --van-primary-color: var(--cp-primary);
    }
    

    App.vue

    <script setup lang="ts"></script>
    
    <template>
      <!-- 验证vant颜色被覆盖 -->
      <van-button type="primary">按钮</van-button>
      <a href="#">123</a>
    </template>
    
    <style scoped lang="scss">
    // 使用 css 变量
    a {
      color: var(--cp-primary);
    }
    </style>
    

    补充内容

1、css变量

有的网站都会有自己的主题色,例如饿了么:

在这里插入图片描述

所以很多元素都会用到这些颜色,例如某些字体颜色,弹框提示颜色等等。

如果每次用到的时候,都使用十六进制的颜色表示,那么效率十分低,并且如果万一有一天需要更换主题颜色,那么一个一个更改,是十分繁琐的。

为了解决以上问题,css引入了变量。

var() 函数用于插入 CSS 变量的值。全局变量可以在整个文档进行访问使用,局部变量只能在声明它的选择器内部使用

例如:

        /* 全局变量 */
        :root {
            --main-color: red;
        }
		 /* 局部变量 */
        .footer {
            --footer-color: green;
        }

:root声明的是全局变量,如果是一个自定义属性-- 作为前缀,使用时比如: var(–main-color)就和red相等。

好处:

  1. 可维护性
    如果没有CSS变量需要手动改变大量的属性值,使用批量处理查找和替换,可能会影响其他样式规则。使用CSS变量只用改变定义时的值。
  2. 提高CSS可读性
    可以通过变量名判断属性内容

注意事项:使用CSS变量要注意大小写敏感,不要把长度的变量用于颜色属性等。

2、实例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 全局变量可以在整个文档进行访问使用 */
        :root {
            --main-color: red;
        }
		 /* 局部变量只能在声明它的选择器内部使用 */
        .footer {
            --footer-color: green;
        }

        .main {
            color: var(--main-color);	/* √ 生效 */
            background-color: var(--footer-color);	/* × 不生效 */
        }

        .footer {
            color: var(--main-color);	/* √ 生效 */
            background-color: var(--footer-color);	/* √ 生效 */
        }
    </style>
</head>

<body>
    <div class="main">
        main
    </div>
    <br />
    <div class="footer">
        footer
    </div>
</body>

</html>

在这里插入图片描述

5、状态管理

用户状态仓库

完成:用户信息仓库创建,提供用户信息,修改用信息,删除用户信息的方法

  • 请求工具需要携带token,访问权限控制需要token,所以用户信息仓库先完成

需求:

  • 用户信息仓库创建
  • 提供用户信息
  • 修改用信息的方法
  • 删除用信息的方法

在这里插入图片描述

代码:

types/user.d.ts

// 用户信息
export type User = {
  // token令牌
  token: string
  // 用户ID
  id: string
  // 用户名称
  account: string
  // 手机号
  mobile: string
  // 头像
  avatar: string
}

stores/user.ts

import { ref } from 'vue'
import { defineStore } from 'pinia'
import type { User } from '@/types/user'

export const useUserStore = defineStore('cp-user', () => {
  // 1.用户信息状态
  const user = ref<User>()
  // 2.设置用户信息函数
  const setUser = (u: User) => {
    user.value = u
  }
  // 3.删除用户信息的函数
  const delUser = () => {
    user.value = undefined
  }

  return {
    user,
    setUser,
    delUser
  }
})

测试App.vue

<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import type { User } from './types/user'

const store = useUserStore()

const login = () => {
  const user: User = {
    token: 'xxx',
    id: '1',
    account: 'xxx',
    mobile: 'xxx',
    avatar: 'xxx'
  }
  store.setUser(user)
}
const logout = () => {
  store.delUser()
}
</script>

<template>
  <div>
    {{ store.user }}
    <van-button type="primary" @click="login">登录</van-button>
    <van-button type="primary" @click="logout">退出</van-button>
  </div>
</template>

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

小结:

  • pinia存储这个数据的意义?
    • 数据共享,提供给项目中任何位置使用
  • 如果存储了数据,刷新页面后数据还在吗?
    • 不在,现在仅仅是js内存中,需要进行本地存储(持久化)

数据持久化

掌握:使用 pinia-plugin-persistedstate 实现pinia仓库状态持久化,且完成测试

参考文档

在这里插入图片描述

  • 安装

    pnpm i pinia-plugin-persistedstate
    # or
    npm i pinia-plugin-persistedstate
    # or
    yarn add pinia-plugin-persistedstate
    
  • main.ts

    import persist from 'pinia-plugin-persistedstate'
    const app = createApp(App)
    
    app.use(createPinia().use(persist))
    
  • 配置 stores/user.ts

    import { ref } from 'vue'
    import { defineStore } from 'pinia'
    import type { User } from '@/types/user'
    
    export const useUserStore = defineStore(
      'cp-user',
      () => {
        // 1.用户信息状态
        const user = ref<User>()
        // 2.设置用户信息函数
        const setUser = (u: User) => {
          user.value = u
        }
        // 3.删除用户信息的函数
        const delUser = () => {
          user.value = undefined
        }
    
        return {
          user,
          setUser,
          delUser
        }
      },
      {
        // 开启本地持久化
        persist: true
      }
    )
    
  • 测试 App.vue

    <script setup lang="ts">
    import { useUserStore } from '@/stores/user'
    import type { User } from './types/user'
    
    const store = useUserStore()
    
    const login = () => {
      const user: User = {
        token: 'xxx',
        id: '1',
        account: 'xxx',
        mobile: 'xxx',
        avatar: 'xxx'
      }
      store.setUser(user)
    }
    const logout = () => {
      store.delUser()
    }
    </script>
    
    <template>
      <div>
        {{ store.user }}
        <van-button type="primary" @click="login">登录</van-button>
        <van-button type="primary" @click="logout">退出</van-button>
      </div>
    </template>
    
    <style scoped lang="scss"></style>
    
    

stores统一导出

如何统一管理?

  1. pinia 独立维护

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

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

  2. 仓库 统一导出

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

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

抽取pinia实例代码,职能单一

stores/index

import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'

// 创建pinia实例
const pinia = createPinia()
// 使用pinia插件
pinia.use(persist)
// 导出pinia实例,给main使用
export default pinia

main.ts

import { createApp } from 'vue'
import pinia from './stores'
import App from './App.vue'
import router from './router'
import './styles/main.scss'

const app = createApp(App)

app.use(pinia)
app.use(router)
app.mount('#app')

统一导出,代码简洁,入口唯一

stores/index

import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'

// 创建pinia实例
const pinia = createPinia()
// 使用pinia插件
pinia.use(persist)
// 导出pinia实例,给main使用
export default pinia

// import { useUserStore } from './modules/user'
// export { useUserStore }
export * from './modules/user'

App.vue

-import { useUserStore } from './stores/user'
+import { useUserStore } from './stores'

6、数据交互

请求工具

在这里插入图片描述

axios配置

创建axios实例

基准地址,超时时间

请求拦截器

携带 token

响应拦截器

业务失败处理,摘取核心响应数据,401处理

业务失败处理&摘取核心响应数据

业务失败处理:

  1. 如何判断业务失败?code 不是 10000
  2. 失败后需要做什么?弹出轻提示,此时返回一个失败的 promise,传递code给catch

摘取核心响应数据:

  1. 现在结果 ===> { data: 响应数据 }
  2. 期望结果 ===> 响应数据

在这里插入图片描述

响应失败-token无效401处理

在这里插入图片描述

代码

实现:token请求头携带,错误响应处理,摘取核心响应数据, 401错误处理

utils/request.ts

模板代码:

import axios from 'axios'

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

instance.interceptors.request.use(
  (config) => {
    // TODO 2. 携带token
    return config
  },
  (err) => Promise.reject(err)
)

instance.interceptors.response.use(
  (res) => {
    // TODO 3. 处理业务失败
    // TODO 4. 摘取核心响应数据
    return res
  },
  (err) => {
    // TODO 5. 处理401错误
    return Promise.reject(err)
  }
)

export default instance

代码实现:

import { useUserStore } from '@/stores'
import axios, { AxiosError } from 'axios'
import router from '@/router'
import { showToast } from 'vant'
import 'vant/es/toast/style'

const instance = axios.create({
  // 1. 基础地址,超时时间
  baseURL: 'https://consult-api.itheima.net/',
  timeout: 10000
})

instance.interceptors.request.use(
  (config) => {
    // 2. 携带token
    const store = useUserStore()
    if (store.user?.token && config.headers) {
      config.headers.Authorization = `Bearer ${store.user.token}`
    }
    return config
  },
  (err) => Promise.reject(err)
)

instance.interceptors.response.use(
  (res) => {
    // 3. 处理业务失败
    // 后台约定,响应成功,但是code不是10000,是业务逻辑失败
    if (res.data.code !== 10000) {
      // 错误提示
      showToast(res.data.message || '业务失败')
      // 返回错误的promise
      // 传入code将来catch的时候可以使用(res.data包含code)
      return Promise.reject(res.data)
    }
    // 4. 摘取核心响应数据
    return res.data
  },
  (err: AxiosError) => {
    // 5. 处理401错误
    if (err.response?.status === 401) {
      // 删除用户信息
      const store = useUserStore()
      store.delUser()
      // 跳转登录,带上接口失效所在页面的地址,登录完成后回跳使用
      router.push({
        path: '/login',
        query: { returnUrl: router.currentRoute.value.fullPath }
      })
    }
    return Promise.reject(err)
  }
)

export default instance

请求函数

函数封装

简化传参逻辑

在这里插入图片描述

添加类型

设置响应数据类型

在这里插入图片描述

扩展:为什么使用axios.request()泛型第二个参数?

在这里插入图片描述

代码

实现:导出一个通用的请求工具函数,支持设置响应数据类型

utils/request.ts

import { useUserStore } from '@/stores'
import axios, { AxiosError, type Method } from 'axios'
import router from '@/router'
import { showToast } from 'vant'
import 'vant/es/toast/style'

const instance = axios.create({
  // 1. 基础地址,超时时间
  baseURL: 'https://consult-api.itheima.net/',
  timeout: 10000
})

instance.interceptors.request.use(
  (config) => {
    // 2. 携带token
    const store = useUserStore()
    if (store.user?.token && config.headers) {
      config.headers.Authorization = `Bearer ${store.user.token}`
    }
    return config
  },
  (err) => Promise.reject(err)
)

instance.interceptors.response.use(
  (res) => {
    // 3. 处理业务失败
    // 后台约定,响应成功,但是code不是10000,是业务逻辑失败
    if (res.data.code !== 10000) {
      // 错误提示
      showToast(res.data.message || '业务失败')
      // 返回错误的promise
      // 传入code将来catch的时候可以使用(res.data包含code)
      return Promise.reject(res.data)
    }
    // 4. 摘取核心响应数据
    return res.data
  },
  (err: AxiosError) => {
    // 5. 处理401错误
    if (err.response?.status === 401) {
      // 删除用户信息
      const store = useUserStore()
      store.delUser()
      // 跳转登录,带上接口失效所在页面的地址,登录完成后回跳使用
      router.push({
        path: '/login',
        query: { returnUrl: router.currentRoute.value.fullPath }
      })
    }
    return Promise.reject(err)
  }
)

export default instance

// 请求工具函数
type Data<T> = {
  code: number
  message: string
  data: T
}
export const request = <T>(
  url: string,
  method: Method = 'GET',
  submitData?: object
) => {
  // 参数:地址,请求方式,提交的数据
  // 返回:promise
  return instance.request<any, Data<T>>({
    url,
    method,
    [method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData
  })
}

测试:

<script setup lang="ts">
import { request } from '@/utils/request'
import type { User } from './types/user'
import { useUserStore } from './stores'

const store = useUserStore()
const login = async () => {
  const res = await request<User>('/login/password', 'POST', {
    mobile: '13211112222',
    password: 'abc12345'
  })
  store.setUser(res.data)
}
</script>

<template>
  <van-button type="primary" @click="login">登录</van-button>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1129577.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboo单机多线程高并发防止重复消费的redis方案

springboo单机多线程高并发防止重复消费的redis方案 仅提供方案与测试。 想法&#xff1a;第一次收到userCode时&#xff0c;检查是否在redis中有&#xff0c;如果有&#xff0c;就表明已经消费了&#xff0c;返回抢单失败&#xff1b;否则&#xff0c;就去消费&#xff0c;顺…

EfficientFormerV2:全新的轻量级视觉Transformer

期刊&#xff1a;2023 IEEE/CVF International Conference on Computer Vision (ICCV) 标题&#xff1a;Rethinking&#xff08;重新审视&#xff09; Vision Transformers&#xff08;ViT&#xff09; for MobileNet Size and Speed&#xff08;MobileNet的规模和速度&#xf…

计算机组成原理-存储器概念

计算机组成原理-存储器 存储系统的基本概念 1.层次结构 可以直接被CPU读取: 高速缓存:cache主存储器: 主存和内存 辅助存储器: 辅存和外存 2.分类 1.按层次结构划分 如上面所示 2.按存储介质 半导体存储器磁表面存储器光存储器 3.按信息可更改性 r/w存储器ROM(只读存储器) 4…

Vue2 + Echarts实现3D地图下钻

一、npm安装组件&#xff1a; "echarts": "5.4.0","echarts-gl": "^2.0.9","element-china-area-data": "^5.0.2", 二、Vue页面 <template><div class"Map3D" id"Map3D" ref"…

Python基础入门例程9-NP9 十六进制数字的大小

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解答&#xff1a; 说明&#xff1a; 描述 计算的世界&#xff0c;除了二进制与十进制&#xff0c;使用最多的就是十六进制了&#xff0c;现在使用input读入一个十六进制的数字&#xff0c;输出它的十进制数字…

30 个常用的 Linux 命令!

作者&#xff1a;JackTian 来源&#xff1a;公众号「杰哥的IT之旅」 ID&#xff1a;Jake_Internet 链接&#xff1a;30 个常用的 Linux 命令&#xff01; 命令 1&#xff1a;last用于显示用户最近登录信息&#xff0c;包括用户名、登录时间、登录来源等信息 单独执行last命令&…

开发直播带货APP:用户体验设计策略

在当今数字化时代&#xff0c;直播带货APP已经成为了电子商务领域的一股重要力量。这种形式的电子商务结合了实时直播和购物&#xff0c;吸引了数百万用户。然而&#xff0c;为了确保直播带货APP的成功&#xff0c;关键在于提供出色的用户体验。本文将探讨开发直播带货APP的用户…

iframe嵌入报表滚动条问题

当在iframe中嵌入报表时&#xff0c;可能会遇到滚动条的问题。下面是一个详细的介绍 1. 了解iframe&#xff1a; - iframe是HTML中的元素&#xff0c;用于在当前页面中嵌入另一个页面。 - 嵌入报表时常使用iframe&#xff0c;以便将报表以独立的方式展示&#xff0c;并与其他页…

MT4教程新手指南:一步步开启你的金融交易之旅!

本文将为您详细介绍如何使用MT4(MetaTrader 4)平台进行金融交易。MT4是全球最受欢迎的在线交易平台之一&#xff0c;它拥有强大的功能&#xff0c;包括图表分析工具、交易执行、订单管理等&#xff0c;可以帮助你更好地理解和参与金融市场。那么&#xff0c;让我们开始吧! **步…

基于正余弦算法的无人机航迹规划-附代码

基于正余弦算法的无人机航迹规划 文章目录 基于正余弦算法的无人机航迹规划1.正余弦搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用正余弦算法来优化无人机航迹规划。 1.正余弦…

31一维信号滤波(限幅滤波、中值滤波、均值滤波、递推平均滤波),MATLAB程序已调通,可直接运行。

一维信号滤波&#xff08;限幅滤波、中值滤波、均值滤波、递推平均滤波&#xff09;&#xff0c;MATLAB程序已调通&#xff0c;可直接运行。 31matlab、中值滤波、信号处理 (xiaohongshu.com)

structs2 重构成SpringBoot架构

# 目录 structs2 重构成SpringBoot架构 1.1 structs2架构&#xff1a; 1.2 springboot 架构 1.3 演化要点&#xff1a; 1.基于前端的展示层不需要修改 2.HttpServlet 将会有SpringBoot annotation 来处理 3.构建前置的Structs url 转发器&#xff0c;适配 4.ActionSupport将由…

9篇论文速览股票预测高分经典方案

作为一直以来的烫门&#xff0c;股票预测因其非线性、高度波动性和复杂性等原因&#xff0c;成为了金融量化领域的一大难题。以往的解决方案主要围绕机器学习展开&#xff0c;如今&#xff0c;基于深度学习的股票预测方法有了许多新的突破。 为了帮助大家更深入地了解股票预测…

防止消息丢失与消息重复——Kafka可靠性分析及优化实践

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

ToDesk等远程软件连接主机无法更改分辨率 - 解决方案

问题 使用ToDesk等远程软件连接自己的Linux或Windows主机时&#xff0c;若主机已连接显示器&#xff0c;则可通过系统设置更改显示分辨率。但如果主机没有连接显示器或显示器的电源关闭&#xff0c;则无法正常调整分辨率。下文介绍解决方案。 解决方案 方案1&#xff1a;连接…

多跳推理真的可解释吗?10.24

多跳推理真的可解释吗 摘要1 引言2 相关工作2.1 多跳推理2.2 基于规则的推理2.3 可解释性评估 3 基础知识4 基准测试4.1 数据集构建4.2 评估框架4.3 近似可解释性评分4.4 Benchmark with Manual Annotation4.5 使用挖掘规则的基准 实验 摘要 近年来&#xff0c;多跳推理在获取…

BUUCTF wireshark 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 黑客通过wireshark抓到管理员登陆网站的一段流量包&#xff08;管理员的密码即是答案) 密文&#xff1a; 下载附件&#xff0c;解压后得到一个.pcap文件。 解题思路&#xff1a; 1、双击文件&#xff0c;在wires…

测试用例的设计方法(全):边界值分析方法

一.方法简介 1.定义&#xff1a;边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充&#xff0c;这种情况下&#xff0c;其测试用例来自等价类的边界。 2.与等价划分的区别 1)边界值分析不是从某等价类中随便挑…

Mysql主从集群同步延迟问题怎么解决

主从复制 复制过程分为几个步骤&#xff1a; 主库的更新事件(update、insert、delete)被写到binlog 从库发起连接&#xff0c;连接到主库。 此时主库创建一个 binlog dump thread&#xff0c;把 binlog 的内容发送到从库。 从库启动之后&#xff0c;创建一个 I/O 线程&#xff…

用VLOOKUP快速合并两个表格

一、前言 上周五微信收到运营提过来的需求&#xff0c;第一句话&#xff1a;帮我提取一下1号门店的库存数据&#xff0c;马上登录系统下载一份库存数据给到他然后专心读代码&#xff0c;过一会微信第二句话&#xff1a;帮我提取一下1号门店商品半年/一年的销量数据&#xff0c…