vite: 搭建完整项目

Vite 可以使用插件进行扩展,这得益于 Rollup 优秀的插件接口设计和一部分 Vite 独有的额外选项。这意味着 Vite 用户可以利用 Rollup 插件的强大生态系统,同时根据需要也能够扩展开发服务器和 SSR 功能。

文章末尾附带源码地址

完整目录

引入依赖

{
  "name": "vite-project",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@types/echarts": "^4.9.22",
    "@types/vue": "^2.0.0",
    "axios": "^1.6.8",
    "echarts": "^5.5.0",
    "element-plus": "^2.7.3",
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.1",
    "vue": "^3.4.21",
    "vue-i18n": "^9.13.1",
    "vue-router": "^4.0.13"
  },
  "devDependencies": {
    "@types/node": "^20.12.8",
    "@vitejs/plugin-legacy": "^5.4.0",
    "@vitejs/plugin-vue": "^5.0.4",
    "eslint": "^9.2.0",
    "postcss-plugin-px2rem": "^0.8.1",
    "prettier": "^3.2.5",
    "sass": "^1.77.1",
    "terser": "^5.31.0",
    "typescript": "^5.2.2",
    "unplugin-auto-import": "^0.17.6",
    "unplugin-element-plus": "^0.8.0",
    "unplugin-vue-components": "^0.27.0",
    "vite": "^5.2.0",
    "vite-plugin-compression": "^0.5.1",
    "vue-tsc": "^2.0.6"
  }
}

vite.config.ts

配置插件

Vite 可以使用插件进行扩展,这得益于 Rollup 优秀的插件接口设计和一部分 Vite 独有的额外选项。

AutoImport和Components插件使用在启动后,会在auto-imports.d.ts和components.d.ts生成对应的组件和方法。

import { resolve } from 'path';
import viteCompression from 'vite-plugin-compression';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
  base: './', // 设置打包路径
  plugins: [
    vue(),
    legacy({
      targets: ['defaults', 'not IE 11']
    }),
    // ElementPlus按需导入
    AutoImport({
      resolvers: [ElementPlusResolver()],
      imports: ['vue', 'vue-router']
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    }),
    // gzip压缩 生产环境生成 .gz 文件
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',
    })
  ]
})

auto-imports.d.ts

/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
  const EffectScope: typeof import('vue')['EffectScope']
  const computed: typeof import('vue')['computed']
  const createApp: typeof import('vue')['createApp']
  const customRef: typeof import('vue')['customRef']
  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
  const defineComponent: typeof import('vue')['defineComponent']
  const effectScope: typeof import('vue')['effectScope']
  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
  const getCurrentScope: typeof import('vue')['getCurrentScope']
  const h: typeof import('vue')['h']
  const inject: typeof import('vue')['inject']
  const isProxy: typeof import('vue')['isProxy']
  const isReactive: typeof import('vue')['isReactive']
  const isReadonly: typeof import('vue')['isReadonly']
  const isRef: typeof import('vue')['isRef']
  const markRaw: typeof import('vue')['markRaw']
  const nextTick: typeof import('vue')['nextTick']
  const onActivated: typeof import('vue')['onActivated']
  const onBeforeMount: typeof import('vue')['onBeforeMount']
  const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
  const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
  const onDeactivated: typeof import('vue')['onDeactivated']
  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
  const onMounted: typeof import('vue')['onMounted']
  const onRenderTracked: typeof import('vue')['onRenderTracked']
  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
  const onScopeDispose: typeof import('vue')['onScopeDispose']
  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
  const onUnmounted: typeof import('vue')['onUnmounted']
  const onUpdated: typeof import('vue')['onUpdated']
  const provide: typeof import('vue')['provide']
  const reactive: typeof import('vue')['reactive']
  const readonly: typeof import('vue')['readonly']
  const ref: typeof import('vue')['ref']
  const resolveComponent: typeof import('vue')['resolveComponent']
  const shallowReactive: typeof import('vue')['shallowReactive']
  const shallowReadonly: typeof import('vue')['shallowReadonly']
  const shallowRef: typeof import('vue')['shallowRef']
  const toRaw: typeof import('vue')['toRaw']
  const toRef: typeof import('vue')['toRef']
  const toRefs: typeof import('vue')['toRefs']
  const toValue: typeof import('vue')['toValue']
  const triggerRef: typeof import('vue')['triggerRef']
  const unref: typeof import('vue')['unref']
  const useAttrs: typeof import('vue')['useAttrs']
  const useCssModule: typeof import('vue')['useCssModule']
  const useCssVars: typeof import('vue')['useCssVars']
  const useLink: typeof import('vue-router')['useLink']
  const useRoute: typeof import('vue-router')['useRoute']
  const useRouter: typeof import('vue-router')['useRouter']
  const useSlots: typeof import('vue')['useSlots']
  const watch: typeof import('vue')['watch']
  const watchEffect: typeof import('vue')['watchEffect']
  const watchPostEffect: typeof import('vue')['watchPostEffect']
  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
  // @ts-ignore
  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
  import('vue')
}

components.d.ts

/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}

/* prettier-ignore */
declare module 'vue' {
  export interface GlobalComponents {
    EchartsComp: typeof import('./src/components/echartsComp.vue')['default']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    InitBar: typeof import('./src/components/echarts/initBar/index.vue')['default']
    LeftA: typeof import('./src/components/echarts/leftA/index.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    Test: typeof import('./src/components/test.vue')['default']
  }
}

配置proxy请求代理

在 Vite 中配置代理请求(proxy)通常用于开发环境,以便将 API 请求代理到后端服务器,避免跨域问题。

export default defineConfig({
  server: {
    proxy: {
      // 字符串简写写法:http://localhost:5173/foo -> http://localhost:4567/foo
      '/foo': 'http://localhost:4567',
      // 带选项写法:http://localhost:5173/api/bar -> http://jsonplaceholder.typicode.com/bar
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
      // 正则表达式写法:http://localhost:5173/fallback/ -> http://jsonplaceholder.typicode.com/
      '^/fallback/.*': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/fallback/, ''),
      },
      // 使用 proxy 实例
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        configure: (proxy, options) => {
          // proxy 是 'http-proxy' 的实例
        }
      },
      // 代理 websockets 或 socket.io 写法:ws://localhost:5173/socket.io -> ws://localhost:5174/socket.io
      '/socket.io': {
        target: 'ws://localhost:5174',
        ws: true,
      },
    },
  },
})

引入scss

  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "./src/styles/var.scss" as *;`,
      },
    }
    ,
    postcss: {
      plugins: [postcssPluginPx2rem(px2remOptions)]
    }
  },

完整配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'
import postcssPluginPx2rem from "postcss-plugin-px2rem"; //引入插件

// 如果编辑器提示 path 模块找不到,则可以安装一下 @types/node -> npm i @types/node -D
import { resolve } from 'path';
import viteCompression from 'vite-plugin-compression';
import ElementPlus from 'unplugin-element-plus/vite';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

//配置参数 
const px2remOptions = {
  rootValue: 19.2,
  unitPrecision: 5,
  mediaQuery: false,  
  minPixelValue: 0
}

// https://vitejs.dev/config/
export default defineConfig({
  base: './', // 设置打包路径
  plugins: [
    vue(),
    legacy({
      targets: ['defaults', 'not IE 11']
    }),
    // ElementPlus按需导入
    AutoImport({
      resolvers: [ElementPlusResolver()],
      imports: ['vue', 'vue-router']
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    }),
    // gzip压缩 生产环境生成 .gz 文件
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',
    })
  ],
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'), // 设置 `@` 指向 `src` 目录
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        // additionalData: `@use "./src/styles/var.scss" as *;`,
      },
    }
    ,
    postcss: {
      plugins: [postcssPluginPx2rem(px2remOptions)]
    }
  },
  build: {
    outDir: 'E:\\code\\默通ERP\\erp-web\\public\\static\\erp-screen',
    // 生产环境去除 console debugger
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    minify: 'terser', // 混淆器,terser构建后文件体积更小
  },
  server: {
    port: 3000, // 设置服务启动端口号
    open: true, // 设置服务启动时是否自动打开浏览器
    proxy: {
      '/api': {
        target: '实际请求地址',	//实际请求地址
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
    }
  }
})

tsconfig.json配置项

{
  "compilerOptions": {
    "target": "ES2020", // 指定编译后的ECMAScript版本
    "useDefineForClassFields": true, //将 class 声明中的字段语义从 [[Set]] 变更到 [[Define]]
    "module": "ESNext", // 指定要使用的模板标准
    "lib": ["ES2020", "DOM", "DOM.Iterable"], // 用来指定需要包含的模块,只有在这里列出的模块声明文件才会被加载进来
    "skipLibCheck": true, // 用来控制是否在编译时进行库文件检查的

    "allowJs": true, // 允许编译JS
    "checkJs": true, // 用来指定是否检查和报告JS文件中的错误
    /* Bundler mode */
    "moduleResolution": "node", //用于选择模块解析策略,有'node'和'classic'两种类型
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true, //包含导入的模块.json的扩展
    "isolatedModules": true, // 指定是否将每个文件作为单独的模块,默认为true,他不可以和declaration同时设定
    "noEmit": true, // noEmit不生成编译文件
    "jsx": "preserve", // 指定jsx代码用于的开发环境
    "paths": {
      "@/*": ["./src/*"]
    },
    /* Linting */
    "strict": true, // 严格模式
    "noUnusedLocals": true, // 用于检查是否有定义了但是没有使用变量
    "noUnusedParameters": true, // 用于检测是否在函数中没有使用的参数
    "noFallthroughCasesInSwitch": true, // 用于检查switch中是否有case没有使用break跳出switch,默认为false
    "allowSyntheticDefaultImports": true, //指定允许从没有默认导出的模块中默认导入
    "forceConsistentCasingInFileNames": true //是否强制代码中使用的模块文件名必须和文件系统中的文件名保持大小写一致
  },

  "include": ["src/**/*.ts",  "src/**/*.js", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts"],
  "references": [{ "path": "./tsconfig.node.json" }],
  "exclude": [
    "node_modules"                    /* 排除 node_modules 目录 */
  ]
}

其他配置项

.gitignore

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.eslintrc.js

// ESlint 检查配置
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint',
    sourceType: 'module'
  },
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  extends: ['plugin:vue/recommended', 'eslint:recommended'],

  // add your custom rules here
  //it is base on https://github.com/vuejs/eslint-config-vue
  rules: {
    "vue/max-attributes-per-line": [2, {
      "singleline": 10,
      "multiline": {
        "max": 1,
        "allowFirstLine": false
      }
    }],
    "vue/singleline-html-element-content-newline": "off",
    "vue/multiline-html-element-content-newline":"off",
    "vue/name-property-casing": ["error", "PascalCase"],
    "vue/no-v-html": "off",
    'accessor-pairs': 2,
    'arrow-spacing': [2, {
      'before': true,
      'after': true
    }],
    'block-spacing': [2, 'always'],
    'brace-style': [2, '1tbs', {
      'allowSingleLine': true
    }],
    'camelcase': [0, {
      'properties': 'always'
    }],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {
      'before': false,
      'after': true
    }],
    'comma-style': [2, 'last'],
    'constructor-super': 2,
    'curly': [2, 'multi-line'],
    'dot-location': [2, 'property'],
    'eol-last': 2,
    'eqeqeq': ["error", "always", {"null": "ignore"}],
    'generator-star-spacing': [2, {
      'before': true,
      'after': true
    }],
    'handle-callback-err': [2, '^(err|error)$'],
    'indent': [2, 2, {
      'SwitchCase': 1
    }],
    'jsx-quotes': [2, 'prefer-single'],
    'key-spacing': [2, {
      'beforeColon': false,
      'afterColon': true
    }],
    'keyword-spacing': [2, {
      'before': true,
      'after': true
    }],
    'new-cap': [2, {
      'newIsCap': true,
      'capIsNew': false
    }],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-caller': 2,
    'no-console': 'off',
    'no-class-assign': 2,
    'no-cond-assign': 2,
    'no-const-assign': 2,
    'no-control-regex': 0,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty-character-class': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': [2, {
      'allowLoop': false,
      'allowSwitch': false
    }],
    'no-lone-blocks': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {
      'max': 1
    }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-symbol': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': [2, 'except-parens'],
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-this-before-super': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unexpected-multiline': 2,
    'no-unmodified-loop-condition': 2,
    'no-unneeded-ternary': [2, {
      'defaultAssignment': false
    }],
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [2, {
      'vars': 'all',
      'args': 'none'
    }],
    'no-useless-call': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-with': 2,
    'one-var': [2, {
      'initialized': 'never'
    }],
    'operator-linebreak': [2, 'after', {
      'overrides': {
        '?': 'before',
        ':': 'before'
      }
    }],
    'padded-blocks': [2, 'never'],
    'quotes': [2, 'single', {
      'avoidEscape': true,
      'allowTemplateLiterals': true
    }],
    'semi': [2, 'never'],
    'semi-spacing': [2, {
      'before': false,
      'after': true
    }],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-unary-ops': [2, {
      'words': true,
      'nonwords': false
    }],
    'spaced-comment': [2, 'always', {
      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
    }],
    'template-curly-spacing': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'wrap-iife': [2, 'any'],
    'yield-star-spacing': [2, 'both'],
    'yoda': [2, 'never'],
    'prefer-const': 2,
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    'object-curly-spacing': [2, 'always', {
      objectsInObjects: false
    }],
    'array-bracket-spacing': [2, 'never']
  }
}

.eslintignore

# 忽略build目录下类型为js的文件的语法检查
build/*.js
# 忽略src/assets目录下文件的语法检查
src/assets
# 忽略public目录下文件的语法检查
public
# 忽略当前目录下为js的文件的语法检查
*.js
# 忽略当前目录下为vue的文件的语法检查
*.vue

.env

.env.production

#生产环境
NODE_ENV=production
VITE_APP_TITLE = "vite大屏设计"
#VITE_APP_PORT = 3001

# 请求接口
VITE_BASE_URL = "http://***:8601"

.env.development


# 开发环境
NODE_ENV = "development"
VITE_APP_TITLE = "vite大屏设计"
#VITE_APP_PORT = 8083

# 请求接口
VITE_BASE_URL = "https://***:8601"

.editorconfig

# Editor configuration, see http://editorconfig.org
root = true

[*]
charset = utf-8
# indent_style = space
indent_size = 2
insert_final_newline = true
# trim_trailing_whitespace = true

[*.md]
max_line_length = 200
trim_trailing_whitespace = false

router路由配置

// router/index.ts  
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'  
  
const routes: Array<RouteRecordRaw> = [  
  {  
    path: '/',  
    name: 'Home',  
    component: () => import('../views/Home.vue')  
  }
  // 其他路由...  
]  
  
const router = createRouter({  
  history: createWebHashHistory(),  
  routes  
})  
  
export default router
// main.ts
import './style.css'
// main.ts  
import { createApp } from 'vue'  
import App from './App.vue'  
import router from './router' // 引入路由配置  
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import '@/utils/rem'
// import * as echarts from 'echarts';//3.当前页面引入

const app = createApp(App)  

// app.config.globalProperties.$echarts = echarts;
// 使用路由  
app.use(router).use(ElementPlus)
  
// 挂载应用  
app.mount('#app')

axios请求完整配置

在utils目录下创建request.ts文件

// utils/request.ts
import { ElMessage, ElLoading  } from "element-plus";
import axios, {
  AxiosInstance,
  InternalAxiosRequestConfig,
  AxiosResponse,
  AxiosError,
} from "axios";
import { useStore } from "../store";
import { ResponseCodeEnum } from "@/enums/httpEnum";
import { ResultData } from "@/api/type/api.type";
import { AxiosCancel } from "./AxiosCancel";

const store = useStore()


const config = {
  baseURL: import.meta.env.VITE_BASE_URL,
  timeout: 5000,
  withCredentials: true,
  headers: {},
};


const message = (text: string, type: any) => {
    ElMessage({
        showClose: true,
        message: text,
        type: type
    })
}

let loading: any = null;
const axiosCancel = new AxiosCancel();

class RequestHttp {
  service: AxiosInstance;

  constructor() {
    this.service = axios.create(config);

    /**
     * @description 请求拦截器
     */
    this.service.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        console.log(config)

        // 打开全局 loading
        // 如不需要全局 loading,则第三个参数  { headers: { noLoading: true } }
        if(!config.headers.noLoading) {
            loading = ElLoading.service({
                lock: true,
                text: 'Loading',
                background: 'rgba(0, 0, 0, 0.7)',
              })
        }

        // 将请求添加到 pending 中
        axiosCancel.addPending(config);

        // 这里如果需要添加token
        const token = store.getToken; 
        config.headers["X-Access-Token"] = token;

        return config;
      }
    );

    /**
     * @description 响应拦截器
     */
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data, config } = response;
        // 关闭全局 loading
        if(!config.headers.noLoading && loading) {
            loading.close();
        }

        // 请求结束,移除本次请求
        axiosCancel.removePending(config.url, config.method);

        // 接口返回 code 不是 200 的处理
        if (data.code !== ResponseCodeEnum.SUCCESS) {
          message(data.msg, 'error');

          // 登录失效,清除 token,跳转到登录页面
          if (data.code === ResponseCodeEnum.NOLOGIN) {
            store.setToken("");
            window.location.href = "/login";
          }

          return Promise.reject(data);
        }
        return data;
      },

      (error: AxiosError) => {
        loading.close();
        const { response } = error;
        if (response) {
          checkStatus(response.status);
        }
        return false;
      }
    );
  }

  // 常用请求方法封装
  get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.get(url, { params, ..._object });
  }
  post<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.post(url, params, _object);
  }
  put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.put(url, params, _object);
  }
  delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
    return this.service.delete(url, { params, ..._object });
  }
}

/**
 * @description: 校验网络请求状态码
 * @param {Number} status
 * @return void
 */
const checkStatus = (status: number): void => {
  switch (status) {
    case 404:
      message("资源不存在!", 'error');
      break;
    case 405:
      message("请求方式错误!", 'error');
      break;
    case 500:
      message("服务器异常!", 'error');
      break;
    default:
      message("请求失败!", 'error');
  }
};


const request = new RequestHttp();
export default request;

在utils目录下创建AxiosCancel.ts文件


import axios, { AxiosRequestConfig, Canceler } from "axios";

const cancelMap = new Map<string, Canceler>();

export class AxiosCancel {
  /**
   * 添加请求的 cancel
   * @param config
   */
  addPending(config: AxiosRequestConfig) {
    const { url, method } = config;

    if (!url || !method) return;

    // 处理同个api,同时多次请求的情况,先移除上一个
    this.removePending(url, method);

    const key = getCancelMapKey(url, method);
    config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
      if (!cancelMap.has(key)) {
        cancelMap.set(key, cancel);
      }
    });
  }

  /**
   * 移除请求
   * @param url
   * @param method
   */
  removePending(url: string | undefined, method: string | undefined) {
    if (!url || !method) return;    

    const key = getCancelMapKey(url, method);
    const cancel = cancelMap.get(key);
    if (cancel) {
      cancel();
      cancelMap.delete(key);
    }
  }

  /**
   * 移除所有请求
   */
  removeAllPending() {
    cancelMap.forEach((cancel) => {
      cancel && cancel();
    });
    cancelMap.clear();
  }
}

function getCancelMapKey(url: string, method: string) {
  return `${url}_${method}`;
}

// services/index.ts
import request from '@/utils/request'

// 
export const getList = (params: any) => {
    return request.get('', params)
}
// api.type.ts
// 接口返回结构,不包含 data
export interface ResponseResult {
    // 状态码
    code: number;
    // 消息
    msg: string;
  }
  
  // 完整的接口返回结构
  export interface ResultData<T = any> extends ResponseResult {
    // 数据
    data: T;
  }
  
  

pinia配置

import { defineStore, acceptHMRUpdate } from "pinia";

export const useStore = defineStore({
    id: 'index',
    state: () => ({
        user: '',
        token: ''
    }),
    getters: {
        getUser: state => {
            return `getters ${state.user}`
        },
        getToken() {
            return window.sessionStorage.getItem('token')
        }
    },
    actions: {
        changeUser(user: any) {
            this.user = user
        },
        setToken(token: string) {
            this.token = token;
            window.sessionStorage.setItem('token', token);
        }
    }
})

注:如果在项目中想使用js,不进行类型校验,在代码顶部加上 “ // @ts-nocheck” 即可

源码地址:https://gitee.com/gitwdd/vite-plateform 

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值