yarn + vue3 + vite + ts + pinia + element-plus
项目目录

安装vite
1. yarn create vite [选择vue + TypeScript]
2. cd project [进入项目]
3. yarn [安装依赖]
4. yarn dev [运行开发服务]
安装scss
yarn add -D scss
自动导入
yarn add -D unplugin-vue-components unplugin-auto-import
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
//自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from "unplugin-vue-components/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ["vue", "vue-router"],
// 存放的位置
dts: "src/auto-import.d.ts",
}),
Components({
// 引入组件的,包括自定义组件
// 存放的位置
dts: "src/components.d.ts",
}),
],
});
配置@别名
yarn add -D @types/node
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 1、 导入 path 模块,帮助我们解析路径
import { resolve } from "path";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
//自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from "unplugin-vue-components/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ["vue", "vue-router"],
// 存放的位置
dts: "src/auto-import.d.ts",
}),
Components({
// 引入组件的,包括自定义组件
// 存放的位置
dts: "src/components.d.ts",
}),
],
//1、 ↓解析配置
resolve: {
// ↓路径别名
alias: {
"@": resolve(__dirname, "./src"),
},
},
});
- tsconfig.json配置baseUrl,paths参数
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* 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": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
vue-router
yarn add vue-router
<template>
<div>
<h1>This is an Home page</h1>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "home",
});
</script>
<style scoped></style>
<template>
<div>
<h1>This is an about page</h1>
</div>
</template>
<script lang="ts">
export default {
name: "about",
};
</script>
<style scoped></style>
import { createRouter, createWebHistory } from "vue-router";
const routes = [
{
path: "/",
name: "Home",
component: () => import("@/views/home.vue"),
},
{
path: "/about",
name: "About",
//使用import可以路由懒加载,如果不使用,太多组件一起加载会造成白屏
component: () => import("@/views/about.vue"),
},
//{
//配置404页面
//path: '/:catchAll(.*)',
//name: '404',
//component: () => import(''),
//}
];
// 路由
const router = createRouter({
history: createWebHistory(),
routes,
});
// 导出
export default router;
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// 1、routes
import router from "@/router/index";
const app = createApp(App);
app.use(router);
app.mount("#app");
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</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" />
<!-- 路由跳转-->
<router-link to="/">首页</router-link>
<br />
<router-link to="/about">关于</router-link>
<router-view></router-view>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
pinia
yarn add pinia
yarn add pinia-plugin-persistedstate
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// 1、routes
import router from "@/router/index";
// 2、pinia
import pinia from "@/store";
const app = createApp(App);
app.use(router);
app.use(pinia);
app.mount("#app");
- store下新建目录modules,并新建demo.ts
import { defineStore } from "pinia";
import { ref } from "vue";
const useDemoStore = defineStore(
"demo",
() => {
const counter = ref(0);
const increment = () => {
counter.value++;
};
return {
counter,
increment,
};
}
// ,
// {
// // 开启持久化,默认使用localStorage
// persist: true,
// }
// ,
// {
// // 开启持久化,更改为sessionStorage存储
// persist: {
// storage:sessionStorage
// },
// }
);
export default useDemoStore;
<template>
<div class="about">
<h1>This is an about page</h1>
<h3>counter: {{ counter }}</h3>
<button @click="add">计数</button>
</div>
</template>
<script setup lang="ts">
import useDemoStore from "@/store/modules/demo";
import { storeToRefs } from "pinia";
const demoStore = useDemoStore();
const { counter } = storeToRefs(demoStore);
const add = () => {
demoStore.increment();
};
</script>
<style scoped>
button {
color: cornflowerblue;
font-size: 30px;
}
</style>
axios
yarn add axios
import axios from "axios";
// 创建axios实例
const request = axios.create({
baseURL: "", // 所有的请求地址前缀部分(没有后端请求不用写)
timeout: 80000, // 请求超时时间(毫秒)
withCredentials: true, // 异步请求携带cookie
// headers: {
// 设置后端需要的传参类型
// 'Content-Type': 'application/json',
// 'token': x-auth-token',//一开始就要token
// 'X-Requested-With': 'XMLHttpRequest',
// },
});
// request拦截器
request.interceptors.request.use(
(config) => {
// 如果你要去localStorage获取token
let token = localStorage.getItem("x-auth-token");
if (token) {
//添加请求头
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
(error) => {
// 对请求错误做些什么
Promise.reject(error);
}
);
// response 拦截器
request.interceptors.response.use(
(response) => {
// 对响应数据做点什么
return response.data;
},
(error) => {
// 对响应错误做点什么
return Promise.reject(error);
}
);
export default request;
import instance from "@/utils/request";
//一般情况下,接口类型会放到一个文件
// 下面两个TS接口,表示要传的参数
interface ReqLogin {
name: string;
paw: string;
}
interface ReqStatus {
id: string;
navStatus: string;
}
// Res是返回的参数,T是泛型,需要自己定义,返回对数统一管理***
type Res<T> = Promise<ItypeAPI<T>>;
// 一般情况下响应数据返回的这三个参数,
// 但不排除后端返回其它的可能性,
interface ItypeAPI<T> {
data: T; //请求的数据,用泛型
msg: string | null; // 返回状态码的信息,如请求成功等
code: number; //返回后端自定义的200,404,500这种状态码
}
// post请求 ,没参数
export const LogoutAPI = (): Res<null> => instance.post("/admin/logout");
// post请求,有参数,如传用户名和密码
export const loginAPI = (data: ReqLogin): Res<string> =>
instance.post("/admin/login", data);
// post请求 ,没参数,但要路径传参
export const StatusAPI = (data: ReqStatus): Res<null> =>
instance.post(`/productCategory?ids=${data.id}&navStatus=${data.navStatus}`);
// get请求,没参数,
export const FlashSessionListApi = (): Res<null> =>
instance.get("/flashSession/list");
// get请求,有参数,路径也要传参 (也可能直接在这写类型,不过不建议,大点的项目会维护一麻烦)
export const ProductCategoryApi = (params: { parentId: number }): Res<any> =>
instance.get(`/productCategory/list/${params.parentId}`, { params });
// get请求,有参数,(如果你不会写类型也可以使用any,不过不建议,因为用了之后 和没写TS一样)
export const AdminListAPI = (params: any): Res<any> =>
instance.get("/admin/list", { params });
<script setup lang="ts">
import {loginAPI} from "@/api/login";
const login= () => {
loginAPI({
...val,
}).then((res) => {
console.log('***',res );
let { list, pageNum, pageSize, total } = res.data
})
};
</script>
- 第二种使用方式:直接使用(和vue2在cretae上用一样,setup自带async,await在顶层可以直接使用)
<script setup lang="ts">
import { loginAPI} from "@/api/login";
//直接使用,一般用在进入页面入请求数据的接口
let res = await loginAPI()
console.log( "***" ,res);
}
</script>
- 第三种使用方式:使用async/await,(setup虽然自带async,但单独用await只能在顶层使用,如果在函数下还是要async/await一起写)
<script setup lang="ts">
import { loginAPI} from "@/api/login";
const search = async(val: IUseTableParam) => {
let res = await loginAPI({
...val,
})
console.log( "***" ,res);
let { list, pageNum, pageSize, total } = res.data
console.log(list, pageNum, pageSize, total);
}
</script>
element-plus
yarn add element-plus
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 1、 导入 path 模块,帮助我们解析路径
import { resolve } from "path";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
// 自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from "unplugin-vue-components/vite";
// 自动导入element-plus
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ["vue", "vue-router"],
// 存放的位置
dts: "src/auto-import.d.ts",
resolvers: [ElementPlusResolver()],
}),
Components({
// 引入组件的,包括自定义组件
// 存放的位置
dts: "src/components.d.ts",
resolvers: [ElementPlusResolver()],
}),
],
//1、 ↓解析配置
resolve: {
// ↓路径别名
alias: {
"@": resolve(__dirname, "./src"),
},
},
});
<template>
<div class="about">
<h1>This is an about page</h1>
<h3>counter: {{ counter }}</h3>
<el-button type="primary" @click="add">计数</el-button>
</div>
</template>
<script setup lang="ts">
import useDemoStore from "@/store/modules/demo";
import { storeToRefs } from "pinia";
const demoStore = useDemoStore();
const { counter } = storeToRefs(demoStore);
const add = () => {
demoStore.increment();
};
</script>
<style scoped></style>
element-plus 自动导入图标
yarn add -D unplugin-icons
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 1、 导入 path 模块,帮助我们解析路径
import { resolve } from "path";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
// 自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from "unplugin-vue-components/vite";
// 自动导入element-plus
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
// 自动导入element-plus图标
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ["vue", "vue-router"],
// 存放的位置
dts: "src/auto-import.d.ts",
resolvers: [
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({
prefix: "Icon",
}),
],
}),
Components({
// 引入组件的,包括自定义组件
// 存放的位置
dts: "src/components.d.ts",
resolvers: [
// 自动注册图标组件
IconsResolver({
enabledCollections: ["ep"],
}),
ElementPlusResolver(),
],
}),
// 自动导入图标组件
Icons({
autoInstall: true,
}),
],
//1、 ↓解析配置
resolve: {
// ↓路径别名
alias: {
"@": resolve(__dirname, "./src"),
},
},
});
<template>
<div class="about">
<h1>This is an about page</h1>
<h3>counter: {{ counter }}</h3>
<el-button type="primary" @click="add">计数</el-button>
<i-ep-edit />
<el-icon :size="30">
<i-ep-edit></i-ep-edit>
</el-icon>
</div>
</template>
<script setup lang="ts">
import useDemoStore from "@/store/modules/demo";
import { storeToRefs } from "pinia";
const demoStore = useDemoStore();
const { counter } = storeToRefs(demoStore);
const add = () => {
demoStore.increment();
};
</script>
<style scoped></style>
element-plus 自动导入中文
/// <reference types="vite/client" />
// 注册element-plus中文模块
declare module "element-plus/dist/locale/zh-cn.mjs";
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
// 引入element-plus中文包
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
</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" />
<!-- 路由跳转 -->
<router-link to="/">首页</router-link>
<br />
<router-link to="/about">关于</router-link>
<!-- element-plus 中文 -->
<el-config-provider :locale="zhCn">
<!-- 添加路由组件 -->
<router-view />
</el-config-provider>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
多项目环境配置
yarn add -D cross-env dotenv fs
--------------.env文件-----------------
# 基础路径
VITE_BASE_URL = './'
NODE_ENV = local
# 输出目录
# VITE_OUTPUT_DIR = 'dist_local'
# 本地接口路径
VITE_API_DOMAIN = 'http://127.0.0.1:8000'
--------------.env.develop文件-----------------
NODE_ENV = develop
# 输出目录
VITE_OUTPUT_DIR = 'dist_develop'
# 测试环境地址
VITE_API_DOMAIN = 'http://my-admin-laravel7.me'
--------------.env.production文件-----------------
NODE_ENV = production
# 输出目录
VITE_OUTPUT_DIR = 'dist_production'
# 正式环境地址
VITE_API_DOMAIN = 'http://my-admin-laravel7.me'
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 1、 导入 path 模块,帮助我们解析路径
import { resolve } from "path";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
// 自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from "unplugin-vue-components/vite";
// 自动导入element-plus
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
// 自动导入element-plus图标
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
// 多环境
import dotenv from "dotenv";
import fs from "fs";
// https://vitejs.dev/config/
export default ({ command, mode }) => {
// 根据不同的环境使用不同的环境变量
let envFiles: string[] = [];
if (command === "serve") {
envFiles = [
/** default file */
`.env`,
];
} else {
envFiles = [
/** default file */
`.env`,
/** mode file */
`.env.${mode}`,
];
}
for (const file of envFiles) {
const envConfig = dotenv.parse(fs.readFileSync(file));
// eslint-disable-next-line guard-for-in
for (const k in envConfig) {
process.env[k] = envConfig[k];
}
}
return defineConfig({
// base: process.env.VITE_BASE_URL,
plugins: [
vue(),
AutoImport({
// 安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ["vue", "vue-router"],
// 存放的位置
dts: "src/auto-import.d.ts",
resolvers: [
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({
prefix: "Icon",
}),
],
}),
Components({
// 引入组件的,包括自定义组件
// 存放的位置
dts: "src/components.d.ts",
resolvers: [
// 自动注册图标组件
IconsResolver({
enabledCollections: ["ep"],
}),
ElementPlusResolver(),
],
}),
// 自动导入图标组件
Icons({
autoInstall: true,
}),
],
// 1、 ↓解析配置
resolve: {
// ↓路径别名
alias: {
"@": resolve(__dirname, "./src"),
},
},
// 构建
build: {
outDir: process.env.VITE_OUTPUT_DIR,
},
define: {
"process.env": {},
},
// server: {
// host: process.env.VITE_HOST,
// port: Number(process.env.VITE_PORT),
// open: false, // 设置服务启动时是否自动打开浏览器
// https: false, // 是否开启 https
// cors: true, // 允许跨域
// // 设置代理,根据我们项目实际情况配置
// proxy: {
// "/api": {
// target: process.env.VITE_API_DOMAIN,
// changeOrigin: true,
// secure: false,
// rewrite: (path) => path.replace("/api/", "/"),
// },
// },
// },
});
};
{
"name": "vue3-vite-ts-yarn",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
// 修改构建命令增加参数
"build:dev": "vue-tsc --noEmit && cross-env vite build --mode develop",
"build:prod": "vue-tsc --noEmit && cross-env vite build --mode production",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.5.0",
"element-plus": "^2.3.14",
"pinia": "^2.1.6",
"pinia-plugin-persistedstate": "^3.2.0",
"vue": "^3.3.4",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.12",
"@types/node": "^20.6.3",
"@vitejs/plugin-vue": "^4.2.3",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"fs": "^0.0.1-security",
"scss": "^0.2.4",
"typescript": "^5.0.2",
"unplugin-auto-import": "^0.16.6",
"unplugin-icons": "^0.17.0",
"unplugin-vue-components": "^0.25.2",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5"
}
}
打包后假设到nginx后,在/about页面刷新报错解决
location / {
try_files $uri $uri/ /index.html; #解决页面刷新404问题
}