Vite + Vue + TypeScript 项目搭建总结
1. 初始项目
官网地址:Vite | 下一代的前端工具链
1.1 项目搭建
pnpm create vite
然后按照提示操作,这里选择的是 Vue + TypeScript,生成的目录结构如下
├── .vscode
│ └── extensions.json
├── public
│ └── vite.svg
├── src
│ ├── assets
│ │ └── vue.svg
│ ├── components
│ │ └── HelloWorld.vue
│ ├── App.vue
│ ├── main.ts
│ ├── style.css
│ └── vite-env.d.ts
├── .gitignore
├── README.md
├── index.html
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
.vscode/extensions.json
:用来配置推荐安装的 VSCode 插件public/vite.svg
:网站图标,可自定义src/
:存放项目源代码
assets/
:存放静态资源,如图片、字体等components/
:存放公共组件App.vue
,根组件main.ts
:入口 TypeScript 文件vite-env.d.ts
:Vite 环境变量.gitignore
:指定被 Git 忽略的文件或文件夹README.md
:描述项目的主要信息index.html
:入口 HTML 文件package.json
:项目的依赖列表和工具配置信息tsconfig.app.json
:定义项目中其他文件的 TypeScript 编译规则tsconfig.json
:tsconfig.node.json
和tsconfig.app.json
最终会被引入到这个文件中tsconfig.node.json
:专门用来配置vite.config.ts
文件的编译规则vite.config.ts
:Vite 配置文件
1.2 配置
官网地址:配置 Vite | Vite 官方中文文档
修改 vite.config.ts
配置文件
1.2.1 服务器配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});
1.2.2 别名
下载 @types/node
依赖
pnpm install @types/node
添加别名
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
export default defineConfig({
resolve: { alias: { '@': resolve(__dirname, 'src') } },
plugins: [vue()],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});
修改 tsconfig.app.json
,添加别名映射
{
"compilerOptions": {
"composite": true, // 是否编译构建引用项目
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", // 指定文件用来存储增量编译信息
"target": "ES2020", // 用于指定编译之后的版本目标
"useDefineForClassFields": true, // 改变类属性声明的行为
"module": "ESNext", // 指定要使用的模块标准
"lib": ["ES2020", "DOM", "DOM.Iterable"], // 指定要包含在编译中的库文件
"skipLibCheck": true, // 跳过声明文件的类型检查
/* Bundler mode */
"moduleResolution": "bundler", // 模块解析策略
"allowImportingTsExtensions": true, // 允许在非 TypeScript 文件中导入 .ts 或 .tsx 文件
"resolveJsonModule": true, // 解析 JSON 模块
"isolatedModules": true, // 是否将每个文件作为单独的模块
"moduleDetection": "force", // 强制 TypeScript 将所有文件视为模块
"noEmit": true, // 不生成编译文件
"jsx": "preserve", // 指定 JSX 代码用于的开发环境
/* Linting */
"strict": true, // 是否启动所有类型检查
"noUnusedLocals": true, // 检查是否有定义但是没有使用的变量
"noUnusedParameters": true, // 检查是否有在函数体中没有使用的参数
"noFallthroughCasesInSwitch": true, // 检查 Switch 中是否有 Case 没有使用 break 跳出
/* Alias */
"baseUrl": ".", // 设置解析非相对模块名称的基本目录
"paths": { "@/*": ["src/*"] } // 设置模块名称到基于 baseUrl 的路径映射
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] // 指定要编译的路径列表
}
2. UnoCSS
官网地址:UnoCSS: The instant on-demand Atomic CSS engine
2.1 安装
pnpm add -D unocss
安装插件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import UnoCSS from 'unocss/vite';
export default defineConfig({
resolve: { alias: { '@': resolve(__dirname, 'src') } },
plugins: [vue(), UnoCSS()],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});
根目录下创建 uno.config.ts
文件
import { defineConfig } from 'unocss';
export default defineConfig({
// ...UnoCSS options
});
添加 virtual:uno.css
到主入口,即 main.ts
文件
import { createApp } from 'vue';
import App from './App.vue';
import 'virtual:uno.css';
createApp(App).mount('#app');
2.2 预设
- Uno preset:默认预设,目前相当于
@unocss/preset-wind
- Wind preset:Tailwind CSS / Windi CSS 兼容预设,继承了
@unocss/preset-mini
- Mini preset:基础预设,只包含最基本的工具
- Icons preset:使用纯 CSS 的任意图标,图标数据源为 Iconify
pnpm add -D @iconify-json/ic
pnpm add -D @iconify-json/mdi
pnpm add -D @iconify-json/line-md
pnpm add -D @iconify-json/twemoji
- …
- Attributify preset:为其他预设启用属性模式
- Typography preset:提供一组可以用于为原生 HTML 添加排版默认值的文本类
- Tagify preset:为其他预设启用标签化模式
修改 uno.config.ts
文件
import {
defineConfig,
presetAttributify,
presetIcons,
presetTagify,
presetTypography,
presetUno,
} from 'unocss';
export default defineConfig({
presets: [
presetUno(),
presetIcons(),
presetAttributify(),
presetTypography(),
presetTagify(),
],
});
3. unplugin-auto-import
GitHub 地址:unplugin/unplugin-auto-import: Auto import APIs on-demand for Vite, Webpack and Rollup
pnpm i -D unplugin-auto-import
安装插件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import UnoCSS from 'unocss/vite';
import AutoImport from 'unplugin-auto-import/vite';
export default defineConfig({
resolve: { alias: { '@': resolve(__dirname, 'src') } },
plugins: [
vue(),
UnoCSS(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dirs: [],
vueTemplate: true,
}),
],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});
当安装了 TypeScript 时,会应用默认配置,生成相关的 ./auto-imports.d.ts
文件,即根目录下,但可以看到在 tsconfig.app.json
里编译路径列表只包含了 src/
下的文件,因此要手动添加进去,或者修改配置自定义生成文件路径
{
"compilerOptions": {
"composite": true, // 是否编译构建引用项目
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", // 指定文件用来存储增量编译信息
"target": "ES2020", // 用于指定编译之后的版本目标
"useDefineForClassFields": true, // 改变类属性声明的行为
"module": "ESNext", // 指定要使用的模块标准
"lib": ["ES2020", "DOM", "DOM.Iterable"], // 指定要包含在编译中的库文件
"skipLibCheck": true, // 跳过声明文件的类型检查
/* Bundler mode */
"moduleResolution": "bundler", // 模块解析策略
"allowImportingTsExtensions": true, // 允许在非 TypeScript 文件中导入 .ts 或 .tsx 文件
"resolveJsonModule": true, // 解析 JSON 模块
"isolatedModules": true, // 是否将每个文件作为单独的模块
"moduleDetection": "force", // 强制 TypeScript 将所有文件视为模块
"noEmit": true, // 不生成编译文件
"jsx": "preserve", // 指定 JSX 代码用于的开发环境
/* Linting */
"strict": true, // 是否启动所有类型检查
"noUnusedLocals": true, // 检查是否有定义但是没有使用的变量
"noUnusedParameters": true, // 检查是否有在函数体中没有使用的参数
"noFallthroughCasesInSwitch": true, // 检查 Switch 中是否有 Case 没有使用 break 跳出
/* Alias */
"baseUrl": ".", // 设置解析非相对模块名称的基本目录
"paths": { "@/*": ["src/*"] } // 设置模块名称到基于 baseUrl 的路径映射
},
// 指定要编译的路径列表
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts"
]
}
4. unplugin-vue-components
GitHub 地址:unplugin/unplugin-vue-components: 📲 On-demand components auto importing for Vue
pnpm i unplugin-vue-components -D
安装插件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import UnoCSS from 'unocss/vite';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
export default defineConfig({
resolve: { alias: { '@': resolve(__dirname, 'src') } },
plugins: [
vue(),
UnoCSS(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dirs: [],
vueTemplate: true,
}),
Components(),
],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});
和 unplugin-auto-import 一样,会应用默认配置,生成相关的 ./components.d.ts
文件,同样将其加入到编译路径列表
{
"compilerOptions": {
"composite": true, // 是否编译构建引用项目
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", // 指定文件用来存储增量编译信息
"target": "ES2020", // 用于指定编译之后的版本目标
"useDefineForClassFields": true, // 改变类属性声明的行为
"module": "ESNext", // 指定要使用的模块标准
"lib": ["ES2020", "DOM", "DOM.Iterable"], // 指定要包含在编译中的库文件
"skipLibCheck": true, // 跳过声明文件的类型检查
/* Bundler mode */
"moduleResolution": "bundler", // 模块解析策略
"allowImportingTsExtensions": true, // 允许在非 TypeScript 文件中导入 .ts 或 .tsx 文件
"resolveJsonModule": true, // 解析 JSON 模块
"isolatedModules": true, // 是否将每个文件作为单独的模块
"moduleDetection": "force", // 强制 TypeScript 将所有文件视为模块
"noEmit": true, // 不生成编译文件
"jsx": "preserve", // 指定 JSX 代码用于的开发环境
/* Linting */
"strict": true, // 是否启动所有类型检查
"noUnusedLocals": true, // 检查是否有定义但是没有使用的变量
"noUnusedParameters": true, // 检查是否有在函数体中没有使用的参数
"noFallthroughCasesInSwitch": true, // 检查 Switch 中是否有 Case 没有使用 break 跳出
/* Alias */
"baseUrl": ".", // 设置解析非相对模块名称的基本目录
"paths": { "@/*": ["src/*"] } // 设置模块名称到基于 baseUrl 的路径映射
},
// 指定要编译的路径列表
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts",
"components.d.ts"
]
}
5. Vue Router
官网地址:Vue Router | Vue.js 的官方路由
pnpm add vue-router@4
创建 src/router/index.ts
文件
import { createRouter, createWebHistory } from 'vue-router';
const routes = [{ path: '/', component: () => import('@/App.vue') }];
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(_to, _from, savedPosition) {
return savedPosition ? savedPosition : { left: 0, top: 0 };
},
});
export default router;
将 src/router/index.ts
添加到主入口
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import 'virtual:uno.css';
createApp(App).use(router).mount('#app');
6. Pinia
官网地址:Pinia | The intuitive store for Vue.js
pnpm install pinia
创建一个 pinia 实例(根 store)并将其传递给应用
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';
import 'virtual:uno.css';
createApp(App).use(router).use(createPinia()).mount('#app');
创建一个 src\pinia\index.ts
文件
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});
7. Axios
官网地址:Axios
pnpm install axios
创建 src\request\index.ts
文件,自定义一个 Axios 实例
import axios from 'axios';
const request = axios.create({
timeout: 5000,
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response) {
switch (error.response.status) {
case 401:
// 未认证
break;
case 403:
// 未授权
break;
case 404:
// 资源未找到
break;
case 408:
// 超时
break;
case 500:
// 服务器错误
break;
default:
// 未知错误
break;
}
}
return Promise.reject(error);
}
);
export default request;
8. Element Plus(可选)
官网地址:一个 Vue 3 UI 框架 | Element Plus
pnpm install element-plus
自动导入
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import UnoCSS from 'unocss/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({
resolve: { alias: { '@': resolve(__dirname, 'src') } },
plugins: [
vue(),
UnoCSS(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dirs: ['src/router', 'src/request'],
vueTemplate: true,
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
});