vite + vue + typscript + pinia + axios + vue-router + elementPlus

npm create vite@latest

创建项目后

npm i
npm run dev
"dependencies": {
    "vue": "^3.4.21"
  },
"devDependencies": {
  "@vitejs/plugin-vue": "^5.0.4",
  "typescript": "^5.2.2",
  "vite": "^5.2.0",
  "vue-tsc": "^2.0.6"
}

需要安装的插件表格(希望需要安装的插件越来越少!!!)

插件名称备注
pinia等同与 vuex
pinia-plugin-persistedstatepinia持久化插件
axiosapi请求库
vue-router路由
elementPlusUI框架
unplugin-auto-import为 Vite、Webpack、Rollup 和 esbuild 按需自动导入 API。支持 TypeScript。
unplugin-vue-componentsVue 的按需组件自动导入。
vite-plugin-compression压缩大文件
vite-plugin-style-import它会根据需要自动导入组件的样式文件
rollup-plugin-visualizer这是一个依赖分析插件,它提供了多种模式的依赖分析,包括直观的视图分析,sunburst(循环层次图,像光谱)、treemap(矩形层次图,看起来比较直观,也是默认参数)、network(网格图,查看包含关系)、raw-data(原数据模式,json格式), list(列表模式)
eslinteslint针对的是javascript,他是一个检测工具,包含js语法以及少部分格式问题,在eslint看来,语法对了就能;保证代码正常允许,格式问题属于其次;
prettierprettier属于格式化工具,它看不惯格式不统一,所以它就把eslint没干好的事接着干,另外,prettier支持,包含js在内的多种语言
@vitejs/plugin-legacy为打包后的文件提供传统浏览器兼容性支持。
unplugin-icons自动按需加载在图标,包括element plus 但是不仅仅时 element plus;使用教程请点击具体包含图标内容查看iconify官网请点击unplugin-icons github请点击

添加基础路径

TS版本需要安装@types/node,不然会警告找不到对应的类型声明

npm install @types/node --save-dev

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
})

引入TS 文件会报错找不到相应类型声明,因为在配置好 vite.config.ts 文件后
tsconfig.json 文件 或者 jsconfig.json 文件也要进行文件系统路径别名设置。需要配置baseUrl和paths字段

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["ESNext", "DOM"],
    "skipLibCheck": true,
    "noEmit": true,
    "types": ["element-plus/global"],
    "baseUrl": "./",  // 解析非相对模块的基础地址,默认是当前目录
    "paths": {
      "@/*": ["./src/*"]  // 路径映射,相对于baseUrl
    }
  },
 
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

安装 pinia

pnpm install --save pinia
npm i pinia-plugin-persistedstate
"dependencies": {
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.1",
    "vue": "^3.4.21"
  },
创建文件夹:src\store\index.ts

主要是创建一个实例

import type { App } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' //数据持久化

const store = createPinia()

store.use(piniaPluginPersistedstate)

export const setupStore = (app: App<Element>) => {
  app.use(store)
}
export { store }
挂载vue
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { setupStore } from '@/store'

const app = createApp(App)
setupStore(app)

app.mount('#app')
使用时:

创建:src/store/modules/user.ts

import { defineStore } from 'pinia'

interface UserState {
  userInfo?: string
  tokenKey: string
  token: string
  roleRouters?: string[]
  rememberMe: boolean
  loginInfo?: string
}

export const useUserStore = defineStore('user', {
  state: (): UserState => {
    return {
      userInfo: undefined,
      tokenKey: '',
      token: '',
      roleRouters: undefined,
      rememberMe: true,
      loginInfo: undefined
    }
  },
  getters: {
    getTokenKey(): string {
      return this.tokenKey
    },
    getToken(): string {
      return this.token
    },
  },
  actions: {
    setTokenKey(tokenKey: string) {
      this.tokenKey = tokenKey
    },
    setToken(token: string) {
      this.token = token
    },
  },
  // persist: true // 一键开启数据持久化,使用默认配置

  // 开启数据持久化,自定义配置
  persist: {
 	// Key 用于引用 storage 中的数据
  	// 这个 Store 将被持久化存储在 localStorage 中的 my-custom-key key 中。
    key: 'my-custom-key', 
    // 将数据持久化到 storage 中,必须具有 getItem: (key: string) => string | null 和 setItem: (key: string, value: string) => void 两个方法。
	// 这个 store 将被持久化存储在 sessionStorage中。
    storage: sessionStorage, 

    // 用于指定 state 中哪些数据需要被持久化。[] 表示不持久化任何状态,undefined 或 null 表示持久化整个 state。
    // 该 store 中, 只有userInfo 和 rememberMe 被持久化,而其他不会被持久化。
    paths: ['userInfo', 'rememberMe'], 

    // 该 hook 将在从 storage 中恢复数据之前触发,并且它可以访问整个 PiniaPluginContext,这可用于在恢复数据之前强制地执行特定的操作。
    beforeRestore: (ctx) => {
      console.log(`即将恢复 '${ctx.store.$id}'`)
    },

    // 该 hook 将在从 storage 中恢复数据之后触发,并且它可以访问整个 PiniaPluginContext,这可用于在恢复数据之后强制地执行特定的操作。
    afterRestore: (ctx) => {
      console.log(`刚刚恢复完 '${ctx.store.$id}'`)
    },

    // 当设置为 true 时,持久化/恢复 Store 时可能发生的任何错误都将使用 console.error 输出。
    debug: true
  },
})


在组件中使用
<script setup>
import {useUserStore} from '@/store/modules/user';
const store = useUserStore();
// ...
store.setTokenKey({...}); // 直接调用是不是很方便
// ...
store.user.page; // 直接获取
</script>
更多详细使用请点击此处

安装axios

pnpm i axios
// 或者
npm i axios

创建文件: src\utils\request.ts

import axios from 'axios';
// 配置新建一个 axios 实例
const service = axios.create({
	baseURL: import.meta.env.VITE_API_URL, // 环境变量
	timeout: 50000,
	headers: { 'Content-Type': 'application/json' },
});
// 添加请求拦截器
service.interceptors.request.use(
	(config) => {
		// 在发送请求之前做些什么
		return config;
	},
	(error) => {
		// 对请求错误做些什么
		return Promise.reject(error);
	}
);

// 添加响应拦截器
service.interceptors.response.use(
	(response) => {
		// 对响应数据做点什么
		const res = response.data;
		const {status, data, message} = response || {};
		if (status !== 200) {
			const {message: messageErr} = service.interceptors.response.data || {};
			return Promise.reject(messageErr);
		} else {
			return {status, data};
		}
	},
	(error) => {
		const {data, status} = error.response || {};
		// 对响应错误做点什么 ...
		return Promise.reject(messageErr);
	}
);
// 导出 axios 实例
export default service;

使用时:
创建

import request from "../../utils/request";

export function userlist(params) {
  return request({
    url: `/userlist`,
    method: "GET",
    params,
  });
}

export function addUser(params) {
  return request({
    url: `/addUser`,
    method: "POST",
    data: {
      ...params
    }
  });
}

在组件中使用

<script setup>
import {deleteRoleApi} from '/@/api/...';
deleteRoleApi(id).then(res => {})
</script>

安装vue-router

pnpm install vue-router --save

创建文件:src\router\index.ts

import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import type { App } from 'vue'
import { NO_RESET_WHITE_LIST } from '@/assets/constants'

const Layout = () => import('@/layout/Layout.vue')

export const constantRouterMap: AppRouteRecordRaw[] = [
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard/analysis',
    name: 'Root',
    meta: {
      hidden: true
    }
  },
  {
    path: '/login',
    component: () => import('@/views/Login/Login.vue'),
    name: 'Login',
    meta: {
      hidden: true,
      noTagsView: true
    }
  },
  {
    path: '/personal',
    component: Layout,
    redirect: '/personal/personal-center',
    name: 'Personal',
    meta: {
      hidden: true,
      canTo: true
    },
    children: [
      {
        path: 'personal-center',
        component: () => import('@/views/Personal/PersonalCenter/PersonalCenter.vue'),
        name: 'PersonalCenter',
        meta: {
          hidden: true,
          canTo: true
        }
      }
    ]
  },
  {
    path: '/404',
    component: () => import('@/views/Error/404.vue'),
    name: 'NoFind',
    meta: {
      hidden: true,
      title: '404',
      noTagsView: true
    }
  }
]

export const asyncRouterMap: AppRouteRecordRaw[] = [
  
  {
    path: '/error',
    component: Layout,
    redirect: '/error/404',
    name: 'Error',
    meta: {
      icon: 'ci:error',
      alwaysShow: true
    },
    children: [
      {
        path: '404-demo',
        component: () => import('@/views/Error/404.vue'),
        name: '404Demo',
        meta: {
          title: '404'
        }
      },
      {
        path: '403-demo',
        component: () => import('@/views/Error/403.vue'),
        name: '403Demo',
        meta: {
          title: '403'
        }
      },
      {
        path: '500-demo',
        component: () => import('@/views/Error/500.vue'),
        name: '500Demo',
        meta: {
          title: '500'
        }
      }
    ]
  },
  
]

const router = createRouter({
  history: createWebHashHistory(),
  strict: true,
  routes: constantRouterMap as RouteRecordRaw[],
  scrollBehavior: () => ({ left: 0, top: 0 })
})

export const resetRouter = (): void => {
  router.getRoutes().forEach((route) => {
    const { name } = route
    if (name && !NO_RESET_WHITE_LIST.includes(name as string)) {
      router.hasRoute(name) && router.removeRoute(name)
    }
  })
}

export const setupRouter = (app: App<Element>) => {
  app.use(router)
}

export default router

挂载到vue

import { createApp } from 'vue'
import '@/style.css'
import App from '@/App.vue'
import { setupStore } from '@/store'
import { setupRouter } from '@/router'

const app = createApp(App)
setupStore(app)
setupRouter(app)
app.mount('#app')

安装scss

直接安装不需要node-sass 和 sass-loader

npm install -D sass

安装 按需加载 element plus

npm install element-plus
安装相关按需加载的插件
npm install -D unplugin-vue-components unplugin-auto-import unplugin-icons vite-plugin-style-import consola

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports: ['vue', 'vue-router'], // 自动引入相关api
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          // 设置自动导入的图标组件前缀
          prefix: 'icon',
          // 标识自定义图标集
          customCollections: ['ci']
        })
      ],
    }),
    Components({
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          // 设置自动导入的图标组件前缀
          prefix: 'icon',
          // 标识自定义图标集
          customCollections: ['ci']
        })
      ],
    }),
    Icons({
      compiler: 'vue3',
      autoInstall: true,
      // 自定义配置
      customCollections: { 
        ci: FileSystemIconLoader('./src/assets/svg', svg => svg.replace(/^<svg /, '<svg fill="currentColor" '))
      }
    }),
    createStyleImportPlugin({
      resolves: [ElementPlusResolve()],
      libs: [
        {
          libraryName: 'element-plus',
          esModule: true,
          resolveStyle: (name) => {
            if (name === 'click-outside') {
              return ''
            }
            return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
          }
        }
      ]
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
})


使用方式,直接用不需要import

<script setup>

import IconBaseline5g from '~icons/ep/edit'

</script>

<template>
  

<!-- 使用component 需要先引用 -->
  <el-icon :size="20">
    <component :is="IconBaseline5g" />
  </el-icon>
  <!--  直接使用,不需要引用 -->
  <!-- icon是头部(上面vite.config.js 配置的prefix: 'icon'-->
  <!-- ep是element plus图标库的简称 edit是图标的名字 -->
  <el-icon :size="20">
    <iconEpEdit />
  </el-icon>
  <!--  使用本地自定义的svg -->
  <!-- icon是头部(上面vite.config.js 配置的prefix: 'icon'-->
  <!-- Ci是自定义图标的集合(上面vite.config.js 配置的customCollections: { 
      ci: FileSystemIconLoader...) vue是自定义目录下的svg文件的名称 -->
  <el-icon :size="20">
    <iconCiVue />
  </el-icon>
</template>

静态资源动态引用:图片

vite官方推荐
使用require的可以用上面的方法替换

@vitejs/plugin-legacy

安装
必须安装 Terser,因为插件遗留版使用 Terser 进行缩小。

npm i -D @vitejs/plugin-legacy terser
// vite.config.js
import legacy from '@vitejs/plugin-legacy'

export default {
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
    }),
  ],
}

vite-plugin-compression 压缩大文件

安装

npm i -D vite-plugin-compression
import viteCompression from "vite-plugin-compression";
plugins: [
			viteCompression({
				verbose: true,
				disable: false,
				deleteOriginFile: false,
				// 文件大于 100Kb 开启压缩 
				threshold: 100000,
				algorithm: "gzip",
				ext: "gz",
			}),
		],

压缩文件需要搭配nginx 配置,详情请点击

rollup-plugin-visualizer 这是一个依赖分析插件

参考链接:https://blog.csdn.net/g18204746769/article/details/127431733
安装

pnpm install vite-plugin-cdn-import --save-dev
import { visualizer } from 'rollup-plugin-visualizer';
plugins: [
	visualizer()
]

vite.config.js全部的配置

想了解配置想请点击

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'
import legacy from '@vitejs/plugin-legacy'
import viteCompression from "vite-plugin-compression";

// https://vitejs.dev/config/
export default defineConfig((mode): any => {
  const env = loadEnv(mode.mode, process.cwd())
  return {
    plugins: [
      vue(),
      AutoImport({
        imports: ['vue', 'vue-router'], // 自动引入相关api
        resolvers: [
          ElementPlusResolver(),
          IconsResolver({
            // 设置自动导入的图标组件前缀
            prefix: 'icon',
            // 标识自定义图标集
            customCollections: ['ci']
          })
        ],
      }),
      Components({
        resolvers: [
          ElementPlusResolver(),
          IconsResolver({
            // 设置自动导入的图标组件前缀
            prefix: 'icon',
            // 标识自定义图标集
            customCollections: ['ci']
          })
        ],
      }),
      Icons({
        compiler: 'vue3',
        autoInstall: true,
        // 自定义配置
        customCollections: { 
          ci: FileSystemIconLoader('./src/assets/svg', svg => svg.replace(/^<svg /, '<svg fill="currentColor" '))
        }
      }),
      createStyleImportPlugin({
        resolves: [ElementPlusResolve()],
        libs: [
          {
            libraryName: 'element-plus',
            esModule: true,
            resolveStyle: (name) => {
              if (name === 'click-outside') {
                return ''
              }
              return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
            }
          }
        ]
      }),
      legacy({
        targets: ['defaults', 'not IE 11'],
      }),
      viteCompression({
        verbose: true,
        disable: false,
        deleteOriginFile: false,
        // 文件大于 100Kb 开启压缩 
        threshold: 100000,
        algorithm: "gzip",
        ext: "gz",
      }),
    ],
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    },
    server: {
      host: '0.0.0.0',
      port: env.VITE_PORT,
      open: false,
      proxy: {
        '/api': {
          target: 'https://gitee.com',
          ws: true,
          changeOrigin: true,
          rewrite: (path:string) => path.replace(/^\/gitee/, ''),
        },
      },
    },
    build: {
      outDir: 'dist',
      chunkSizeWarningLimit: 1500,
      rollupOptions: {
        output: {
          entryFileNames: `assets/[name].${new Date().getTime()}.js`,
          chunkFileNames: `assets/[name].${new Date().getTime()}.js`,
          assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`,
          compact: true,
          // manualChunks: {
          // 	vue: ['vue', 'vue-router', 'pinia'],
          // 	echarts: ['echarts'],
          // antvG6: ['@antv/g6'],
          // elementPlus: ['element-plus'],
          // },
          manualChunks(id:string[]) {
            // if (id.includes("style.css")) {
            // 	// 需要单独分割那些资源 就写判断逻辑就行
            // 	return 'src/style.css';
            // }
            // if (id.includes("HelloWorld.vue")) {
            // 	// 单独分割hello world.vue文件
            // 	return 'src/components/HelloWorld.vue';
            // }
            // // 最小化拆分包
            if (id.includes('node_modules')) {
              return id
                .toString()
                .split('node_modules/')[1]
                .split('/')[0]
                .toString()
            }
          },
        },
      },
      minify: 'terser',
      terserOptions: {
        compress: {
          drop_console: mode.mode === 'prd',
          drop_debugger: mode.mode === 'prd',
        },
      },
      sourcemap: mode.mode !== 'prd',
    },
    css: {
      devSourcemap: true,
      preprocessorOptions: { css: { charset: false } },
    },
  }
})

找不到模块“@/xxx/xxx.vue”或其相应的类型声明。ts(2307)

配置tsconfig.json:确保你的tsconfig.json文件中包含了对.vue文件的支持。

{
  "compilerOptions": {
    "types": ["vue/vue3"] // 如果你使用的是Vue 3
    // 或者 "types": ["vue/vue2"] 如果你使用的是Vue 2
  }
}

创建类型声明文件(如果自动处理不起作用):如果自动处理不起作用,你可以手动创建一个声明文件,例如shims-vue.d.ts,并在其中添加以下内容

declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

安装eslint && prettier

根据个人需求,我参考的这几个链接:
https://blog.csdn.net/m0_53022813/article/details/137379423
https://juejin.cn/post/7118294114734440455#heading-1
https://blog.csdn.net/weixin_43459866/article/details/124249172
https://blog.csdn.net/weixin_43993776/article/details/132564724
无法找到模块 vite-plugin-eslint 插件中的 TS 声明文件,隐式含有 “any” 类型。
vscode使用eslint

源码

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值