缘由:由于不经常创建新项目导致每次都忘,在这做个记录,再用的时候就直接翻翻,不用再查了
这里插一句,vite.config.ts 会被多次编辑,不用每次复制,结尾会贴一份完整的代码
路由守卫待续......
可能还有其他也待续......
写了一天写不动了......
1.先初始化项目
1.1 选择用vite构建,输入下面命令
npm create vite@latest
1.2 会问你是否继续,直接回车,默认为yes
1.3 输入项目名字
1.4 输入package.json中的名字,不能有大写
1.5 选择你所用的框架,用上下方向键选择,然后回车,我这里用了vue
1.6 选择用什么类型来开发,也是用上下方向键选择,然后回车,我这里用了TypeScript
1.7 然后就创建好了
1.8 然后跟着命令跑起来就行了,这是完整的截图
2.配置UI element-plus(点击这里查看文档)
2.1 先下载UI
npm install element-plus --save
2.2 然后配置按需导入
npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
// ...
plugins: [
vue(),
+ AutoImport({
+ resolvers: [ElementPlusResolver()],
+ }),
+ Components({
+ resolvers: [ElementPlusResolver()],
+ }),
],
})
2.3 自定义一个主题
2.3.1 先下载所需要的依赖
npm i @types/node scss -D
2.3.2 创建一个scss文件,路径为 src/assets/css/element.scss
// styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #ff7e00,
),
),
);
// 如果只是按需导入,则可以忽略以下内容。
// 如果你想导入所有样式:
// @use "element-plus/theme-chalk/src/index.scss" as *;
2.3.3 配置 vite.config.ts ,顺便把@也一起配置了
+ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver(
+ {importStyle: 'sass'}
)],
}),
],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ },
+ css: {
+ preprocessorOptions: {
+ scss: {
+ additionalData: `@use "@/assets/css/element.scss" as *;`,
+ }
+ }
+ }
})
3.配置unocss(点击这里查看文档)
3.1 下载依赖
npm i unocss @unocss/preset-rem-to-px -D
3.2 创建unocss.config.ts
import { defineConfig, presetUno, presetAttributify } from 'unocss';
// presetAttributify 属性化模式,属性冲突时,可以通过默认un-前缀来解决:<div m-2 rounded text-teal-400 >代替class</div>
import presetRemToPx from '@unocss/preset-rem-to-px';
// 此处我用了rem to px的预设,所以后面不加单位的数字的话会直接被转化为px(如果不用这个预设,就需要用m-100px之类的)
export default defineConfig({
presets: [presetUno(), presetAttributify(), presetRemToPx({ baseFontSize: 4 })],
rules: [
[/^fs-(\d+)$/, ([, num]) => ({ 'font-size': `${num}px` })],
[/^lh-(\d+)$/, ([, num]) => ({ 'line-height': `${num}px` })]
],
variants: [
matcher => {
if (!matcher.startsWith('hover:')) {
return matcher;
}
return {
matcher: matcher.slice(6),
selector: s => `${s}:hover`
};
}
],
shortcuts: {
'center': 'flex items-center justify-center'
}
});
3.3 配置 vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+ import Unocss from 'unocss/vite'
export default defineConfig({
plugins: [
vue(),
+ Unocss({
+ configFile: '@/unocss.config.ts'
+ }),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({
importStyle: 'sass'
})],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/css/element.scss" as *;`,
}
}
}
})
3.4 配置main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
+ import 'uno.css'
createApp(App).mount('#app')
4.配置vue-router
4.1 先下载依赖
npm install vue-router@4 -D
4.2 在src下创建 router/index.ts
import { createRouter,createWebHashHistory } from "vue-router";
import login from '@/views/login/login.vue'
import home from '@/views/home/home.vue'
let routes = [
{
path:'/',
redirect:'/login'
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/home',
name: 'home',
component: home
}
]
const router = createRouter({
history:createWebHashHistory(),
routes
})
export default router
4.3 在 main.ts 中配置
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import 'uno.css'
+ import router from '@/router/index.ts'
- createApp(App).mount('#app')
+ createApp(App).use(router).mount('#app')
4.4 如果用了ts报错找不到类型啥的,在根目录下的 vite-env.d.ts 添加下面这个
/// <reference types="vite/client" />
+ declare module "*ts"
+ declare module "*vue"
4.5 路由守卫 路由守卫先待续......
const WhiteList = ['/login']; // 配置白名单
router.beforeEach((to, from, next)=> {
if (WhiteList.indexOf(to.fullPath) !== -1) { // 判断前往页面是都存在于白名单
next()
} else {
if (localStorage.getItem('userInfo')) {// 判断用户是否d登录
if (to.name) { // 判断是否存在路由的name名字,如果有则代表有路由表已经存在,name继续前往,反之,则不存在
next()
}
else {// 当路由不存在,录取路由,动态添加路由表,
let localRoutes: any = localStorage.getItem('routes');
let recursionList = recursionParam(JSON.parse(localRoutes));
for (let item of recursionList) {
router.addRoute(item)
}
next({ ...to, replace: true })
}
} else {
next('/login')
}
}
});
5.配置交互axios
5.1先设置环境变量+代理
5.1.1 根目录下创建.env.production和.env.development
# .env.development
VITE_APP_MODE = "development"
#base api
VITE_APP_BASE_API = "/aaa/"
# .env.production
VITE_APP_MODE = "production"
#base api
VITE_APP_BASE_API = "https://xxx.xxx.com/"
5.1.2 vite.config.ts 中配置代理
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Unocss from 'unocss/vite'
export default defineConfig({
plugins: [
vue(),
Unocss({
configFile: '@/unocss.config.ts'
}),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({
importStyle: 'sass'
})],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/css/element.scss" as *;`,
}
}
},
+ server: {
+ proxy: {
+ // 字符串代理
+ '/xxx': {
+ target: 'https://xxx.xxx.com/',
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/xxx/, '')
+ }
+ }
+ }
})
5.2 配置axios
5.2.1 先下载axios
npm i axios qs -D
5.2.2 在src/utils下创建 api.js 编写 axios 的统一方法
import axios from "axios"
import qs from "qs"
import { ElMessage } from 'element-plus'
let key = "loading";
const _LOGIN_URL = import.meta.env.VITE_APP_LOGIN_URL;
const instance = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 6000,
});
axios.interceptors.response.use((res) => {
// return
if (res.status != 200) {
ElMessage.error("暂无网络,请稍后重试");
ElMessage.destroy(key);
return;
}
if (!res.data.code || res.config.headers.status == 3) {
ElMessage.destroy(key);
return res.data;
}
if (res.data.code == 301) {
ElMessage.error("身份已过期,请重新登录");
ElMessage.destroy(key);
let expireTime = setTimeout(() => {
window.location.href = _LOGIN_URL;
clearTimeout(expireTime);
}, 3000);
return;
}
if (res.data.code == 313) {
ElMessage.error("未登录,请登录");
ElMessage.destroy(key);
let notLoginTime = setTimeout(() => {
window.location.href = _LOGIN_URL;
clearTimeout(notLoginTime);
}, 3000);
return;
}
ElMessage.destroy(key);
return res.data;
},
(error) => {
return error;
}
);
instance.interceptors.request.use((config) => {
console.log(config);
return config;
}, (error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
const requestApi = (method, url, params, status, headers) => {
// status 0: 都显示 1: 不显示loading 2: 不显示报错 3: 都不显示
if (status != 1 || status != 3) {
message.loading({ content: "加载中", key: key, duration: 0 });
}
headers.status = status;
return new Promise((resolve, reject) => {
instance({
method: method,
headers: headers,
url,
params: method == "GET" && params,
data: method == "POST" && qs.stringify(params)
}).then(res => {
resolve(res.data);
}).catch(err => {
reject(err);
});
})
}
const login = (params) => requestApi("POST", "/login", params, 0, { "Content-Type": "application/x-www-form-urlencoded" });
export {
login
}
6. 配置pinia
6.1 先下载所需的依赖
pinia-plugin-persistedstate是为了将store存在本地存储中,这样刷新页面关键信息也不会丢失
npm install pinia pinia-plugin-persistedstate -D
6.2 然后在 main.ts 中配置
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import 'uno.css'
import router from '@/router/index.ts'
+ import { createPinia } from "pinia";
+ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
- createApp(App).use(router).mount('#app')
+ const app = createApp(App);
+ const store = createPinia();
+ store.use(piniaPluginPersistedstate);
+ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+ app.component(key, component)
+ }
+ app.use(router).use(store).mount("#app");
6.3 如何配置
简单举个例子,创建文件,路径为 src/store/user.ts
import { defineStore } from "pinia"
let _LOGIN_URL = import.meta.env.VITE_APP_LOGIN_URL; // 从环境变量中拿到当前应该跳转到哪个地址
export const useUserStore = defineStore('user', {
state: () => {
return {
token: '', // 用户token
userInfo: {}, // 用户信息
}
},
persist: {
storage: sessionStorage, // 这里设置存储方式,也可以是 localStorage
paths: ['userInfo', 'token'], // 要存储的哪个字段
},
actions: {
setToken(token: string) {
this.token = token; // 修改
},
setUserInfo(userInfo: any) {
this.userInfo = userInfo;
},
logout() {
window.sessionStorage.clear();
window.location.href = _LOGIN_URL;
// 调用方法,这里只能把存储里的删掉,pinia本身还需要多加一步$reset()来清除,这个后面有体现
}
}
})
6.4 如何调用操作
在vue文件中,举个例子,可以结合自己需求去完成
<script setup lang="ts">
import { useUserStore } from "@/store/user";
const userStore: any = useUserStore();
onMounted(() => {
if (!userStore.token) { // 这里直接就能使用
// 没有登录信息
router.push({
name: "login",
});
return;
}
// 这里举个例子怎么使用
// 1. 调用事件去修改
let token = "xajxbnajsbxjasbxhsbxhsabxjhbsajhbxa";
userStore.setToken(token);
// 2. 本身自带的方法去修改
userStore.$patch((state) => {
state.token = token;
})
// 3.直接修改不推荐,能不用就不用吧
userStore.token = token;
})
const logout = () => {
// 退出登录
userStore.$reset(); // 清空本身store中所有数据
userStore.logout(); // 调用方法
};
</script>
7. 结尾
7.1 vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Unocss from 'unocss/vite'
export default defineConfig({
plugins: [
vue(),
Unocss({
configFile: '@/unocss.config.ts'
}),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({
importStyle: 'sass'
})],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/css/element.scss" as *;`,
}
}
},
server: {
proxy: {
// 字符串代理
'/huipaia': {
target: 'https://ai.huipaia.com/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/huipaia/, '')
},
}
}
})
7.2 main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import 'uno.css'
import router from '@/router/index.ts'
import { createPinia } from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
createApp(App).use(router).mount('#app')
const app = createApp(App);
const store = createPinia();
store.use(piniaPluginPersistedstate);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(router).use(store).mount("#app");