vue3-vite-ts-eslint

一:创建项目 github完整配置地址

使用vite官网命令安装

pnpm create vite my-vue-ts --template vue-ts
cd my-vue-ts
pnpm install
pnpm dev

tsconfig.json中新增 "allowJs": true, // 允许项目中使用js,对于不习惯使用ts的可以在项目中使用js

二:新增.env文件 修改package.json scripts命令

新增 .env.dev .env.beta .env.prod 文件,文件内容如下,每个文件域名应该不一样,用以请求配置axios时区分不同的环境域名 更多环境配置参考官网

VUE_APP_BASE_API = 'http://127.0.0.1:8080/'

package.json 中scripts下配置如下命令

    "serve:dev": "vite --mode dev",
    "serve:beta": "vite --mode beta",
    "serve:prod": "vite --mode prod",
    "build:dev": "vite build --mode dev",
    "build:beta": "vite build --mode beta",
    "build:prod": "vite build --mode prod",

三:vite.config.js 配置

1.常用配置 vite官网地址

安装依赖:pnpm add @types/node -D
vite.config.js配置如下:

import { resolve } from 'path'
import { defineConfig } from 'vite'
export default defineConfig(({ command, mode }) => {
  return {
    base: './', // 开发或生产环境服务的公共基础路径。
    server: {
      host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
      port: 5173,
    },
    envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
    resolve: {
      alias: { // 配置别名
        '~': resolve(__dirname, '.'),
        '@': resolve(__dirname, 'src'),
        '/#': resolve(__dirname, 'types'),
      },
      extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
        '.mjs',
        '.js',
        '.mts',
        '.ts',
        '.jsx',
        '.tsx',
        '.json',
        '.vue',
      ],
    },
  }
})

2.配置plugins

2.1 vue,vueJsx插件配置

安装依赖
pnpm add @vitejs/plugin-vue
pnpm add @vitejs/plugin-vue-jsx
vite.config.js 配置如下:

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'
export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
    }
})

tips:配置vueJsx时需要加上

    esbuild: {
        jsxFactory: 'h',
        jsxFragment: 'Fragment',
    },

写个jsx测试下
components/helloJsx.jsx

import { defineComponent } from 'vue'
export default defineComponent({ render() { return <div>Test jsx</div> }})

App.vue中引入:
tips:引入时没加后缀,是因为常用配置中配置了extensions:[".jsx",''.vue"],在配置数组中的引入时都可以不加后缀

<script setup lang="ts">
import HelloWorld from './components/HelloWorld'
import helloJsx from './components/helloJsx'
</script>
<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
  <helloJsx />
</template>

2.2配置AutoImport(按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API)和Components(Vue的按需组件自动导入。)

安装依赖
pnpm add unplugin-auto-import - D
pnpm add unplugin-vue-components - D
vite.config.js 配置如下:

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'

export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
            AutoImport({ // 按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API。具有 TypeScript 支持。
                imports: [
                  'vue',
                ],
                dts: 'auto-imports.d.ts', // 生成文件的位置
              }),
              Components({ // Vue的按需组件自动导入。
                dts: 'components.d.ts', // 生成文件的位置
              }),
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
    }
})

AutoImport里面现在就配置了一个vue,现在跟vue相关的api就不用自己去导入了,比如上面jsx文件中的defineComponent

import { defineComponent } from 'vue'
export default defineComponent({ render() { return <div>Test jsx</div> }})

现在只需要写成

export default defineComponent({ render() { return <div>Test jsx</div> }})

dts配置了输出文件的位置,当你执行pnpm dev 时会在你的项目下新建auto-imports.d.ts和components.d.ts
在这两个文件中你能看到它自动帮你引入了的api和组件,比如defineComponent在auto-imports.d.ts中如下导入:

  const defineComponent: typeof import('vue')['defineComponent']

Components插件会默认帮我们导入src/components下的组件 比如HelloWorld在components.d.ts导入如下:

 HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']

如果不想导入可以配置下只需要导入的目录:dirs

Components({ // Vue的按需组件自动导入。
                dirs: ['library/components'],
                dts: 'components.d.ts', // 生成文件的位置
              }),

2.2.1 配合ElementPlus 按需导入使用

安装依赖
pnpm add element-plus
vite.config.js 配置如下:

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'


export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
            AutoImport({ // 按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API。具有 TypeScript 支持。
                imports: [
                  'vue',
                ],
                resolvers: [ElementPlusResolver()],
                dts: 'auto-imports.d.ts', // 生成文件的位置
              }),
              Components({ // Vue的按需组件自动导入。
                dirs: ['library/components'],
                resolvers: [ElementPlusResolver()],
                dts: 'components.d.ts', // 生成文件的位置
              }),
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
    }
})

unplugin-vue-components 提供了ElementPlusResolver 来按需导入具体可以参考 element-pluss官网

2.3 配置vite-plugin-svg-icons和remixicon,导入自定义svg和remixicon图标库配合使用

安装依赖
pnpm add vite-plugin-svg-icons
pnpm add remixicon
vite.config.js 配置如下:
main.ts中先引入以下代码,不引入不现实svg图标

main.ts
import 'virtual:svg-icons-register' // 引入svg
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'



export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
            AutoImport({ // 按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API。具有 TypeScript 支持。
                imports: [
                    'vue',
                ],
                resolvers: [ElementPlusResolver({ importStyle: false })],
                dts: 'auto-imports.d.ts', // 生成文件的位置
            }),
            Components({ // Vue的按需组件自动导入。
                dirs: ['library/components'],
                resolvers: [ElementPlusResolver()],
                dts: 'components.d.ts', // 生成文件的位置
            }),
            createSvgIconsPlugin({ // svg 导入
                iconDirs: [resolve('src/icons/svg')],
                symbolId: 'icon-[name]',
            }),
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
    }
})

createSvgIconsPlugin:
iconDirs:需要导入的自定义个svg图标目录
symbolId: 'icon-[name]' 给图标增加前缀icon

2.3.1写个icon的通用组件

<template>
  <img v-if="isExternal" class="img-icon" :src="icon" />
  <svg v-else-if="isCustomSvg" aria-hidden="true" :class="svgClass">
    <use :xlink:href="'#icon-' + icon" />
  </svg>
  <!-- 内置svg雪碧图较大,对性能要求苛刻的用户请勿使用isDefaultSvg属性 -->
  <svg v-else-if="isDefaultSvg" class="vab-icon">
    <use :xlink:href="remixIconPath + '#ri-' + icon" />
  </svg>
  <i
    v-else
    aria-hidden="true"
    :class="{
      ['ri-' + icon]: true,
    }"
  />
</template>

<script>
  import 'remixicon/fonts/remixicon.css'
  import { isExternal } from '@/utils/validate'
  // import { computed } from 'vue'

  export default {
    name: 'VabIcon',
    props: {
      icon: {
        type: String,
        required: true,
      },
      // 是否使用自定义图标
      isCustomSvg: {
        type: Boolean,
        default: false,
      },
      // 是否使用本地库Remix图标
      isDefaultSvg: {
        type: Boolean,
        default: false,
      },
      className: {
        type: String,
        default: '',
      },
    },
    setup(props) {
      const svgClass = computed(() => {
        if (props.className) return `vab-icon ${props.className}`
        else return 'vab-icon'
      })

      return {
        svgClass,
        isExternal: isExternal(props.icon),
        remixIconPath: import.meta.glob(
          'remixicon/fonts/remixicon.symbol.svg',
          { eager: true }
        ),
      }
    },
  }
</script>

<style lang="scss" scoped>
  .img-icon {
    display: inline-block;
    width: 2em;
    height: 2em;
    vertical-align: middle;
  }

  .vab-icon {
    display: inline-block;
    width: 16px;
    height: 16px;
    margin: 0 auto;
    overflow: hidden;
    vertical-align: middle;
    fill: currentColor;
  }

  [class*='ri'] {
    display: inline-block;
    font-size: 16px;
    text-align: center;
    vertical-align: -3.5px;
  }
</style>
validate.ts
/**
 * @description 判读是否为外链
 * @param path
 * @returns {boolean}
 */
export function isExternal(path: string) {
  return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}

使用:main.ts中全局注册组件

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import vabIcon from '@/components/VabIcon'
import 'virtual:svg-icons-register' // 引入svg


const app = createApp(App)
app.component('vabIcon',vabIcon)
app.mount('#app')

使用自己的svg图标
自己找个svg图标复制到 src/icons/svg 目录下,isCustomSvg表示使用自己导入的svg标志
项目中直接使用: <vabIcon icon="404" isCustomSvg />
使用remixicon图标库的图标 图标库地址
复制图标名字直接使用:<vab-icon icon="arrow-left-up-line" />

2.4 配置eslintPlugin

安装依赖
pnpm add eslint -D
vue,vite相关插件
pnpm add eslint-plugin-vue -D Vue.js的官方ESLint插件。
pnpm add vite-plugin-eslint -D vite的ESLint插件。
ts相关插件:
pnpm add @typescript-eslint/eslint-plugin -D
pnpm add @typescript-eslint/parser -D
pnpm add @vue/eslint-config-typescript -D
prettier相关插件:
pnpm add eslint-config-prettier -D
pnpm add eslint-plugin-prettier -D
vite.config.js 配置如下:

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

import { visualizer } from 'rollup-plugin-visualizer'

import eslintPlugin from 'vite-plugin-eslint'



export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
            AutoImport({ // 按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API。具有 TypeScript 支持。
                imports: [
                    'vue',
                ],
                resolvers: [ElementPlusResolver({ importStyle: false })],
                dts: 'auto-imports.d.ts', // 生成文件的位置
            }),
            Components({ // Vue的按需组件自动导入。
                dirs: ['library/components'],
                resolvers: [ElementPlusResolver()],
                dts: 'components.d.ts', // 生成文件的位置
            }),
            createSvgIconsPlugin({ // svg 导入
                iconDirs: [resolve('src/icons/svg')],
                symbolId: 'icon-[name]',
            }),
            eslintPlugin({ // eslint 校验
                include: [
                    'src/**/*.js',
                    'src/**/*.ts',
                    'src/**/*.vue',
                    'src/**/*.jsx',
                    'src/**/*.tsx',
                    'src/*.js',
                    'src/*.vue',
                ],
            }),
            visualizer({ // 可视化并分析Rollup包,以查看哪些模块占用了空间。
                // 必须放在最后一个
                filename: 'stats.html',
                open: true,
                gzipSize: true,
                sourcemap: true,
            }),
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
        optimizeDeps: {
            include: [
                'axios',
                'element-plus',
                'lodash',
                'echarts',
                'pinia',
                'qs',
            ],
            exclude: [],
        },
        build: {
            outDir: 'dist',
            assetsDir: 'assets',
            cssCodeSplit: true,
            rollupOptions: {
                output: {
                    chunkFileNames: 'static/js/[name]-[hash].js', // 引入文件名的名称
                    entryFileNames: 'static/js/[name]-[hash].js', // 包的入口文件名称
                    assetFileNames: 'static/[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
                    manualChunks: {
                        vue: ['vue'],
                        vueRouter: ['vue-router'],
                        qs: ['qs'],
                        axios: ['axios'],
                        lodash: ['lodash'],
                        echarts: ['echarts'],
                        elementUi: ['element-plus'],
                    },
                },
            },
            //   关闭文件计算
            // reportCompressedSize: false,
            sourcemap: command === 'serve',
            // chunkSizeWarningLimit: 1500 // 消除打包大小超过500kb警告
        },
    }
})

eslintPlugin({ // eslint 校验
include: [
'src//.js',
'src/
/.ts',
'src//.vue',
'src/
/.jsx',
'src//.tsx',
'src/
.js',
'src/*.vue',
],
}),
配置需要校验的文件

.eslintrc.cjs如下
规则参考 https://eslint.nodejs.cn/docs/latest/rules/
eslint-plugin-vue https://eslint.vuejs.org/rules/first-attribute-linebreak.html
typescript-eslint https://typescript-eslint.io/rules/

module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    '@vue/typescript/recommended',
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: {
      ecmaVersion: 12,
      js: 'espree',
      ts: '@typescript-eslint/parser',
      '<template>': 'espree',
    },
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      tsx: true,
    },
    warnOnUnsupportedTypeScriptVersion: false,
  },
  // 规则参考 https://eslint.nodejs.cn/docs/latest/rules/
  // eslint-plugin-vue https://eslint.vuejs.org/rules/first-attribute-linebreak.html
  //typescript-eslint https://typescript-eslint.io/rules/
  rules: {
    'no-undef': 'off',
    'no-console': 'off',
    'no-debugger': 'off',
    'prefer-rest-params': 'off',
    'vue/no-reserved-component-names': 'off',
    'vue/one-component-per-file': 'off',
    'prefer-const': 'off',
    'vue/require-prop-types': 'off',
    'vue/require-explicit-emits': 'off',
    'vue/multi-word-component-names':'off',
    '@typescript-eslint/no-explicit-any': 'off'
  },
}

.eslintignore如下

auto-imports.d.ts
components.d.ts
node_modules
src/assets
src/icons
public
dist

tsconfig.json如下:
tsconfig.json中新增 "allowJs": true, // 允许项目中使用js,对于不习惯使用ts的可以在项目中使用js

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,
    "allowJs": true, // 允许项目中使用js

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "baseUrl": ".",
    
 /* paths */
    "paths": {
      "~/*": ["*"],
      "@/*": ["./src/*"],
      "/#/*": ["./types/*"],
    },
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/components/helloJsx.jsx"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

jsconfig.json 如下:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "paths": {
      "~/*": ["*"],
      "@/*": ["src/*"],
      "/#/*": ["types/*"],
    },
    "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
  },
  "exclude": ["node_modules", "dist"]
}

2.5 配置rollup-plugin-visualizer 可视化并分析Rollup包,以查看哪些模块占用了空间。

安装依赖
pnpm add rollup-plugin-visualizer -D
vite.config.js 配置如下:

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { defineConfig } from 'vite'

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

import { visualizer } from 'rollup-plugin-visualizer'



export default defineConfig(({ command, mode }) => {
    return {
        base: './', // 开发或生产环境服务的公共基础路径。
        server: {
            host: true, // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            port: 5173,
        },
        envPrefix: 'VUE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。
        resolve: {
            alias: { // 配置别名
                '~': resolve(__dirname, '.'),
                '@': resolve(__dirname, 'src'),
                '/#': resolve(__dirname, 'types'),
            },
            extensions: [ // 导入时想要省略的扩展名列表。注意,不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
                '.mjs',
                '.js',
                '.mts',
                '.ts',
                '.jsx',
                '.tsx',
                '.json',
                '.vue',
            ],
        },
        plugins: [
            vue(), // 支持 vue
            vueJsx(), // 支持 jsx tsx
            AutoImport({ // 按需自动导入 Vite、Webpack、Rspack、Rollup 和 esbuild 的 API。具有 TypeScript 支持。
                imports: [
                    'vue',
                ],
                resolvers: [ElementPlusResolver({ importStyle: false })],
                dts: 'auto-imports.d.ts', // 生成文件的位置
            }),
            Components({ // Vue的按需组件自动导入。
                dirs: ['library/components'],
                resolvers: [ElementPlusResolver()],
                dts: 'components.d.ts', // 生成文件的位置
            }),
            createSvgIconsPlugin({ // svg 导入
                iconDirs: [resolve('src/icons/svg')],
                symbolId: 'icon-[name]',
            }),
            visualizer({ // 可视化并分析Rollup包,以查看哪些模块占用了空间。
                // 必须放在最后一个
                filename: 'stats.html', //输出文件名
                open: true,
                gzipSize: true,
                sourcemap: true,
              }),
        ],
        esbuild: {
            jsxFactory: 'h',
            jsxFragment: 'Fragment',
        },
    }
})

visualizer({ // 可视化并分析Rollup包,以查看哪些模块占用了空间。
// 必须放在最后一个
filename: 'stats.html', //输出文件名
open: true,
gzipSize: true,
sourcemap: true,
})
现在运行 pnpm build:serve 就会自动生成stats.html,就可以分析各个包的大小

3.配置optimizeDeps

        optimizeDeps: {
            include: [
                'axios',
                'element-plus',
                'lodash',
                'echarts',
                'pinia',
                'qs',
            ],
            exclude: [],
        },

配置include预购建 vite官网

4.配置build

        build: {
            outDir: 'dist',
            assetsDir: 'assets',
            cssCodeSplit: true,
            rollupOptions: {
                output: {
                    chunkFileNames: 'static/js/[name]-[hash].js', // 引入文件名的名称
                    entryFileNames: 'static/js/[name]-[hash].js', // 包的入口文件名称
                    assetFileNames: 'static/[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
                    manualChunks: {
                        vue: ['vue'],
                        vueRouter: ['vue-router'],
                        qs: ['qs'],
                        axios: ['axios'],
                        lodash: ['lodash'],
                        echarts: ['echarts'],
                        elementUi: ['element-plus'],
                    },
                },
            },
            //   关闭文件计算
            // reportCompressedSize: false,
            sourcemap: command === 'serve',
            // chunkSizeWarningLimit: 1500 // 消除打包大小超过500kb警告
        },

配置如下代码会在打包的dist/static下面生成js,png,css,svg等格式的文件夹单独存放

                    chunkFileNames: 'static/js/[name]-[hash].js', // 引入文件名的名称
                    entryFileNames: 'static/js/[name]-[hash].js', // 包的入口文件名称
                    assetFileNames: 'static/[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等

配置如下代码会把包含在里面的包,打包时生成单独的文件, vueRouter: ['vue-router'],前面的名字可以自己随便取,后面的文件要跟package.json中的一致

manualChunks: {
                        vue: ['vue'],
                        vueRouter: ['vue-router'],
                        qs: ['qs'],
                        axios: ['axios'],
                        lodash: ['lodash'],
                        echarts: ['echarts'],
                        elementUi: ['element-plus'],
                    },

(四)配置pinia

安装依赖:
pnpm add pinia
新建以下文件:

src/store/index.ts
import {createPinia} from 'pinia'
import { App } from 'vue'

const pinia = createPinia()

export function setupStore(app: App<Element>) {
  app.use(pinia)
}

export default pinia
src/store/modules/acl.ts
import { defineStore } from 'pinia'
export const useAclStore = defineStore('acl', {
  state: () => ({
    admin: false,
    role: '2312',
  }),
  getters: {
    getAdmin: (state) => state.admin,
    getRole: (state) => state.role,
  },
  actions: {
    setFull(admin: boolean) {
      this.admin = admin
    },
  },
})

main.ts中引入

import { createApp } from 'vue'
import { setupStore } from './store/index'

import './style.css'
import App from './App.vue'
const app = createApp(App)
setupStore(app)
app.mount('#app')

helloWord.vue中使用

<script setup lang="ts">

import { useAclStore } from '../store/modules/acl'
const { getRole } = useAclStore()
defineProps<{ msg: string }>()

</script>

<template>
  {{getRole}}
  <vabIcon
    icon="404"
    is-custom-svg
  />
  <vab-icon icon="lock-password-line" />
  <vab-icon icon="arrow-right-up-line" />
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

(五)配置router

安装依赖:
pnpm add vue-router
新建以下文件:

src/router/index.ts

import type { RouteRecordRaw } from 'vue-router'
import type { VabRouteRecord } from '/#/router'

import type { App } from 'vue'
import {
  createRouter,
  createWebHashHistory,
  createWebHistory,
} from 'vue-router'

export const constantRoutes: VabRouteRecord[] = [
  {
    path: '/home',
    name: 'Home',
    component: () => import('@/views/Home/index.vue'),
    meta: {
      title:'Home'
    },
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/404.vue'),
    meta: {
      title:'404'
    },
  },
]
const publicPath = ''
const isHashRouterMode = true
const router = createRouter({
  history: isHashRouterMode
    ? createWebHashHistory(publicPath)
    : createWebHistory(publicPath),
  routes: constantRoutes as RouteRecordRaw[],
})
export function setupRouter(app: App<Element>) {
  app.use(router)
  return router
}
export default router

我这里新建了2个路由home和404,你们需要根据自己需求新建路由

跟目录下新建types,项目中用到了ts需要定义接口(interface)

types/router.d.ts

import type { RouteRecordRaw } from 'vue-router'
/**
 * 路由记录
 */
declare interface VabRouteRecord
  extends Omit<RouteRecordRaw, 'name' | 'meta' | 'children'> {
  name: string
  meta: VabRouteMeta
  children?: VabRouteRecord[]
}
declare interface VabRouteMeta {
  // 菜单、面包屑、多标签页显示的名称
  title?: string
}

main.ts引入

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { setupStore } from '@/store'
import { setupRouter } from '@/router'
import vabIcon from '@/components/VabIcon'
import 'virtual:svg-icons-register' // 引入svg


const app = createApp(App)
app.component('VabIcon',vabIcon)
setupStore(app)
setupRouter(app)
app.mount('#app')

现在还需要修改App.vue,加上router-view组件,具体的可以根据自己项目结构修改

<template>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>

现在启动项目就可以输入路由访问了:http://10.1.40.62:5173/#/home
现在只是一个路由的基础配置,后续我会加上erp后台权限管理的逻辑

(六)配置axios

安装依赖:
pnpm add axios
新建以下文件:

src/utils/request.ts

import axios from 'axios'
/**
 * axios请求拦截器配置
 * @param config
 * @returns {any}
 */
const requestConf: any = (config: any): any => {
  const token = '' // 从自己保存的token位置获取token
  if (token) config.headers['token'] = `${token}`
  return config
}

/**
 * axios响应拦截器
 * @param config 请求配置
 * @param data response数据
 * @param status HTTP status
 * @param statusText HTTP status text
 * @returns {Promise<*|*>}
 */
const handleData = async (res: any): Promise<any | any> => {
  const { data, config } = res
  if (config.export) return data //导出
  const { code } = data.heads
  switch (code) {
    case 200:
      return data.body
    case 4001:
      break
    case 403:
      break
    default:
      return Promise.reject(data)
      break
  }
}

/**
 * @description axios初始化
 */
const instance = axios.create({
  baseURL: import.meta.env.VUE_APP_BASE_API,
  timeout: 60000,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
  },
})

/**
 * @description axios请求拦截器
 */
instance.interceptors.request.use(requestConf, (error) => {
  return Promise.reject(error)
})

/**
 * @description axios响应拦截器
 */
instance.interceptors.response.use(
  (response) => handleData(response),
  (error) => {
   //这里可以写提示错误信息的逻辑
    return Promise.reject(error)
  }
)

export default instance

请求拦截和响应拦截里面具体逻辑需要根据自己的具体业务来
axios配置好了,下面就可以写接口了:

src/api/home.ts
import request from '@/utils/request'
export const   getHomeInfo = (data:any) => {
  return request({
    url: '/home/info',
    method: 'post',
    data,
  })
}

组件中引入使用

<script setup lang="ts">
import {getHomeInfo} from '@/api/home'
const getInfo = async ()=>{
 const data = await getHomeInfo({})
 console.log(data)
}
onMounted(() => {
  getInfo()
})
</script>

(七)配置lint-staged 和 husky

安装依赖:
pnpm add husky -D
pnpm add lint-staged -D
pnpm add @commitlint/cli -D
pnpm add @commitlint/config-conventional -D
修改package.josn:

{
  "name": "my-vue3-ts-template",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "serve:dev": "vite --mode dev",
    "serve:beta": "vite --mode beta",
    "serve:prod": "vite --mode prod",
    "build:dev": "vite build --mode dev",
    "build:beta": "vite build --mode beta",
    "build:prod": "vite build --mode prod",
    "lint": "eslint --fix --ext .js,.ts,.jsx,.tsx,.vue src",
    "preview": "vite preview",
    "prepare": "husky install"
  },
  "dependencies": {
    "@vitejs/plugin-vue-jsx": "^3.0.2",
    "axios": "^1.5.0",
    "element-plus": "^2.3.12",
    "pinia": "^2.1.6",
    "remixicon": "^3.5.0",
    "vite-plugin-svg-icons": "^2.0.1",
    "vue": "^3.3.4",
    "vue-router": "^4.2.4"
  },
  "devDependencies": {
    "@commitlint/cli": "^17.7.1",
    "@commitlint/config-conventional": "^17.7.0",
    "@types/node": "^20.5.7",
    "@typescript-eslint/eslint-plugin": "^6.5.0",
    "@typescript-eslint/parser": "^6.5.0",
    "@vitejs/plugin-vue": "^4.3.4",
    "@vue/eslint-config-typescript": "^11.0.3",
    "eslint": "^8.48.0",
    "eslint-plugin-vue": "^9.17.0",
    "husky": "^8.0.3",
    "lint-staged": "^14.0.1",
    "rollup-plugin-visualizer": "^5.9.2",
    "sass": "^1.66.1",
    "typescript": "^5.0.2",
    "unplugin-auto-import": "^0.16.6",
    "unplugin-vue-components": "^0.25.1",
    "vite": "^4.4.5",
    "vite-plugin-eslint": "^1.8.1",
    "vue-tsc": "^1.8.5"
  },
  "lint-staged": {
    "*.{js,cjs,jsx,tsx,vue}": [
      "eslint --fix --ext .js,.vue src"
    ]
  }
}

新增lint-staged配置和 "prepare": "husky install"
lint-staged下是通过eslint来校验的,需要先配置eslint,参考上面eslint配置

跟目录下新增.commitlintrc.cjs

// https://github.com/conventional-changelog/commitlint/#what-is-commitlint
module.exports = {
    extends: ['@commitlint/config-conventional']
  }

运行 pnpm install 通过配置的 脚本 "prepare": "husky install"
会自动在跟目录下新增.husky文件夹

生成好后在.husky文件夹下面新增commit-msg 和 pre-commit 分别如下:

commit-msg

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint --edit $1

pre-commit

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm lint

pre-commit下我是直接运行 pnpm lint 命令来在提交时做校验

好了,现在可以按照commitlint规则来提交代码了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容