javascript
npm create vite@latest
创建项目后
html
npm i
npm run dev
javascript
"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-persistedstate | pinia持久化插件 |
axios | api请求库 |
vue-router | 路由 |
elementPlus | UI框架 |
unplugin-auto-import | 为 Vite、Webpack、Rollup 和 esbuild 按需自动导入 API。支持 TypeScript。 |
unplugin-vue-components | Vue 的按需组件自动导入。 |
vite-plugin-compression | 压缩大文件 |
vite-plugin-style-import | 它会根据需要自动导入组件的样式文件 |
rollup-plugin-visualizer | 这是一个依赖分析插件,它提供了多种模式的依赖分析,包括直观的视图分析,sunburst(循环层次图,像光谱)、treemap(矩形层次图,看起来比较直观,也是默认参数)、network(网格图,查看包含关系)、raw-data(原数据模式,json格式), list(列表模式) |
eslint | eslint针对的是javascript,他是一个检测工具,包含js语法以及少部分格式问题,在eslint看来,语法对了就能;保证代码正常允许,格式问题属于其次; |
prettier | prettier属于格式化工具,它看不惯格式不统一,所以它就把eslint没干好的事接着干,另外,prettier支持,包含js在内的多种语言 |
@vitejs/plugin-legacy | 为打包后的文件提供传统浏览器兼容性支持。 |
unplugin-icons | 自动按需加载在图标,包括element plus 但是不仅仅时 element plus;使用教程请点击,具体包含图标内容查看iconify官网请点击,unplugin-icons github请点击 |
添加基础路径
TS版本需要安装@types/node,不然会警告找不到对应的类型声明
javascript
npm install @types/node --save-dev
vite.config.ts
javascript
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
javascript
{
"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
javascript
pnpm install --save pinia
npm i pinia-plugin-persistedstate
javascript
"dependencies": {
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21"
},
创建文件夹:src\store\index.ts
主要是创建一个实例
javascript
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
javascript
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
javascript
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
},
})
在组件中使用
html
<script setup>
import {useUserStore} from '@/store/modules/user';
const store = useUserStore();
// ...
store.setTokenKey({...}); // 直接调用是不是很方便
// ...
store.user.page; // 直接获取
</script>
更多详细使用请点击此处
安装axios
javascript
pnpm i axios
// 或者
npm i axios
创建文件: src\utils\request.ts
javascript
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;
使用时:
创建
javascript
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
}
});
}
在组件中使用
html
<script setup>
import {deleteRoleApi} from '/@/api/...';
deleteRoleApi(id).then(res => {})
</script>
安装vue-router
javascript
pnpm install vue-router --save
创建文件:src\router\index.ts
javascript
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
javascript
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
javascript
npm install -D sass
安装 按需加载 element plus
javascript
npm install element-plus
安装相关按需加载的插件
javascript
npm install -D unplugin-vue-components unplugin-auto-import unplugin-icons vite-plugin-style-import consola
vite.config.ts
javascript
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
javascript
<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>
静态资源动态引用:图片
使用require的可以用上面的方法替换
@vitejs/plugin-legacy
安装
必须安装 Terser,因为插件遗留版使用 Terser 进行缩小。
javascript
npm i -D @vitejs/plugin-legacy terser
javascript
// vite.config.js
import legacy from '@vitejs/plugin-legacy'
export default {
plugins: [
legacy({
targets: ['defaults', 'not IE 11'],
}),
],
}
vite-plugin-compression 压缩大文件
安装
javascript
npm i -D vite-plugin-compression
javascript
import viteCompression from "vite-plugin-compression";
plugins: [
viteCompression({
verbose: true,
disable: false,
deleteOriginFile: false,
// 文件大于 100Kb 开启压缩
threshold: 100000,
algorithm: "gzip",
ext: "gz",
}),
],
rollup-plugin-visualizer 这是一个依赖分析插件
参考链接:https://blog.csdn.net/g18204746769/article/details/127431733
安装
javascript
pnpm install vite-plugin-cdn-import --save-dev
javascript
import { visualizer } from 'rollup-plugin-visualizer';
plugins: [
visualizer()
]
vite.config.js全部的配置
javascript
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文件的支持。
javascript
{
"compilerOptions": {
"types": ["vue/vue3"] // 如果你使用的是Vue 3
// 或者 "types": ["vue/vue2"] 如果你使用的是Vue 2
}
}
创建类型声明文件(如果自动处理不起作用):如果自动处理不起作用,你可以手动创建一个声明文件,例如shims-vue.d.ts,并在其中添加以下内容
javascript
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