Yang第一次不使用脚手架手动搭建项目
- ✅Vue3.2x+Vite2.x+Typescript+Axios+Element Plus+Pinia+Vue-router4.x+Less+Vueuse
- ✅自动导入插件
- ✅简易版用ts封装axios,未使用类,感觉和js差别不大
完整配置在最后
项目搭建:
创建vite项目
npm init vite@latest
更改vue2/vue3快捷模板
vscode:文件-->文件-->首选项-->用户片段
vue-router4
src/router/index.ts
----------------------------------------------------------------------------
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'login',
component: () => import('../components/login.vue') //一定要有.vue后缀
},
{
path: '/main',
name: 'main',
component: () => import("../components/main.vue") //一定要有.vue后缀
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
--------------------------------------------------------------------------------
main.ts
--------------------------------------------------------------------------------
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index';//绝对不要加.ts后缀
createApp(App).use(router).mount("#app");
--------------------------------------------------------------------------------
组件中调用api
---------------------------------------------------------------------------------
import router from '../router';
router.push('/main')
pinia
src/store/index.ts
---------------------------------------------------------------------------------
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
count: 0,
}
},
getters: {
},
actions: {
}
})
---------------------------------------------------------------------------------
main.ts
---------------------------------------------------------------------------------
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index';
import { createPinia } from 'pinia'
createApp(App).use(router).use(createPinia()).mount("#app");
---------------------------------------------------------------------------------
组件中使用
---------------------------------------------------------------------------------
import {useStore} from './store/index';
let store =useStore()
store.count++
Axios
以登录注册请求封装为例
network/network.ts
---------------------------------------------------------------------------------
import axios from "axios";
export const request = axios.create({
baseURL: "https://more.atcumt.com/",
timeout: 5000,
});
---------------------------------------------------------------------------------
network/login/login.ts
---------------------------------------------------------------------------------
import { ElMessage } from "element-plus";
import { request } from "../network";
interface getBody {
studentId: string;
}
interface postBody {
studentId: string;
password: string;
}
export function getrequest(param: string) {
return request.get(param);
}
export function postrequest(param: string, body: postBody) {
return request.post(param, body);
}
request.interceptors.request.use((config) => {
if (window.localStorage.getItem("token") != "null") {
config!.headers!.Authorization = `Bearer ${window.localStorage.getItem(
"token"
)}`;
}
console.log(config!.headers!.Authorization);
return config;
});
request.interceptors.response.use(
(res) => {
if (res.status == 200) {
console.log(res);
if (res.data.code == 200) {
ElMessage.success({
message: res.data.message,
center: true,
});
return res;
} else if (res.data.code == 1003) {
ElMessage.error({
message: res.data.message,
center: true,
});
return;
} else if (res.data.code == 1002) {
ElMessage({
message: res.data.message,
center: true,
});
return;
} else if (res.data.code == 1001) {
console.log("1001");
ElMessage({
message: res.data.message,
center: true,
});
return;
}
} else {
console.log("error");
ElMessage.error({
message: res.data.message,
center: true,
});
}
},
(err: any) => {
ElMessage.error({
message: "请检查网络设置",
center: true,
});
return;
}
);
---------------------------------------------------------------------------------
全局路由守卫
---------------------------------------------------------------------------------
router.beforeEach((to,from,next) => {
if (to.name !== 'login')
{
console.log('qwq');
if (window.localStorage.getItem('userId')!=null)
{
store.getUserInfo(window.localStorage.getItem("userId") as string);
}
next()
}
else if (to.name !== 'login' && window.localStorage.getItem('token') == 'null')
{
console.log(window.localStorage.getItem("token"));
console.log('to login');
next({name:'login'})
}
else {
next()
}
})
路由守卫+pinia+axios存储用户信息 //localStorage存储get参数,pinia存信息
---------------------------------------------------------------------------------
import { defineStore } from "pinia";
import { getrequest } from "../network/login/login";
interface userInfo {
studentId?: string;
nickname?: string;
avatar?: string;
sex?: string;
description?: string;
follower?: number;
fan?: number;
isFollow?: boolean | undefined;
}
let userInfo: userInfo = {
studentId: "",
nickname: "",
avatar: "",
sex: "",
description: "",
follower: 0,
fan: 0,
isFollow: undefined,
};
export const useStore = defineStore("main", {
state: () => {
return {
userInfo,
};
},
getters: {},
actions: {
getUserInfo(studentId: string) {
getrequest(`user/info/${studentId}`).then((res) => {
this.userInfo.studentId = res.data.data.userInfo.studentId;
this.userInfo.nickname = res.data.data.userInfo.nickname;
this.userInfo.avatar = res.data.data.userInfo.avatar;
this.userInfo.sex = res.data.data.userInfo.sex;
this.userInfo.description = res.data.data.userInfo.description;
this.userInfo.fan = res.data.data.userInfo.fan;
});
},
},
});
Vueuse
npm i @vueuse/core
无需配置,开箱即用
完整配置
Vue3.2x+Vite2.x+Typescript+Axios+Element Plus+Pinia+Vue-router4.x+Vueuse项目完整配置
创建项目
npm init vite@latest
----------------------------------------
npm i
npm install element-plus --save
npm install -D unplugin-vue-components unplugin-auto-import
npm install vue-router@4
npm install pinia
npm install axios
npm install less less-loader --save-dev //less
npm i @vueuse/core
main.ts
----------------------------------------------------------------------
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index';
import { createPinia } from 'pinia'
createApp(App).use(router).use(createPinia()).mount("#app");
----------------------------------------------------------------------
router/index.ts
----------------------------------------------------------------------
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'login',
component: () => import('../components/login.vue'),
},
{
path: '/main',
name: 'main',
component: () => import("../components/main.vue"),
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
----------------------------------------------------------------------
store/index.ts
----------------------------------------------------------------------
路由守卫+pinia+axios存储用户信息 //localStorage存储get参数,pinia存信息
---------------------------------------------------------------------------------
import { defineStore } from "pinia";
import { getrequest } from "../network/login/login";
interface userInfo {
studentId?: string;
nickname?: string;
avatar?: string;
sex?: string;
description?: string;
follower?: number;
fan?: number;
isFollow?: boolean | undefined;
}
let userInfo: userInfo = {
studentId: "",
nickname: "",
avatar: "",
sex: "",
description: "",
follower: 0,
fan: 0,
isFollow: undefined,
};
export const useStore = defineStore("main", {
state: () => {
return {
userInfo,
};
},
getters: {},
actions: {
getUserInfo(studentId: string) {
getrequest(`user/info/${studentId}`).then((res) => {
this.userInfo.studentId = res.data.data.userInfo.studentId;
this.userInfo.nickname = res.data.data.userInfo.nickname;
this.userInfo.avatar = res.data.data.userInfo.avatar;
this.userInfo.sex = res.data.data.userInfo.sex;
this.userInfo.description = res.data.data.userInfo.description;
this.userInfo.fan = res.data.data.userInfo.fan;
});
},
},
});
----------------------------------------------------------------------
network/network.ts
---------------------------------------------------------------------------------
import axios from "axios";
export const request = axios.create({
baseURL: "https://more.atcumt.com/",
timeout: 5000,
});
---------------------------------------------------------------------------------
network/login/login.ts
---------------------------------------------------------------------------------
import { ElMessage } from "element-plus";
import { request } from "../network";
interface getBody {
studentId: string;
}
interface postBody {
studentId: string;
password: string;
}
export function getrequest(param: string) {
return request.get(param);
}
export function postrequest(param: string, body: postBody) {
return request.post(param, body);
}
request.interceptors.request.use((config) => {
if (window.localStorage.getItem("token") != "null") {
config!.headers!.Authorization = `Bearer ${window.localStorage.getItem(
"token"
)}`;
}
console.log(config!.headers!.Authorization);
return config;
});
request.interceptors.response.use(
(res) => {
if (res.status == 200) {
console.log(res);
if (res.data.code == 200) {
ElMessage.success({
message: res.data.message,
center: true,
});
return res;
} else if (res.data.code == 1003) {
ElMessage.error({
message: res.data.message,
center: true,
});
return;
} else if (res.data.code == 1002) {
ElMessage({
message: res.data.message,
center: true,
});
return;
} else if (res.data.code == 1001) {
console.log("1001");
ElMessage({
message: res.data.message,
center: true,
});
return;
}
} else {
console.log("error");
ElMessage.error({
message: res.data.message,
center: true,
});
}
},
(err: any) => {
ElMessage.error({
message: "请检查网络设置",
center: true,
});
return;
}
);
---------------------------------------------------------------------------------
全局路由守卫
---------------------------------------------------------------------------------
router.beforeEach((to,from,next) => {
if (to.name !== 'login')
{
console.log('qwq');
if (window.localStorage.getItem('userId')!=null)
{
store.getUserInfo(window.localStorage.getItem("userId") as string);
}
next()
}
else if (to.name !== 'login' && window.localStorage.getItem('token') == 'null')
{
console.log(window.localStorage.getItem("token"));
console.log('to login');
next({name:'login'})
}
else {
next()
}
})
vite.config.js //自动导入插件配置
----------------------------------------------------------------------
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()],
}),]
})
----------------------------------------------------------------------
踩坑
Element Plus的css无故丢失?
正确操作1/25 找到css的CDN文件扒下来
//index.html
<link rel="stylesheet" href="./src/assets/css/index.css" />
::v-deep报错
改写法弃用,转化为 :deep( )
动态img :src不加载
vite中不要使用绝对路径!打包有问题
//webpack
require('url')
//vite
new URL('url', import.meta.url).href
请求用户信息请求头格式
Bearer空格token
请求用户信息存储
local存get请求的参数,pinia存用户信息
Element Plus的icon只能手动按需引入
TS中遍历对象给变量赋值报错
解决方法:在tsconfig.json中添加
"suppressImplicitAnyIndexErrors":true,
echarts图标部署到服务器二次渲染失效
解决方法每次挂载前都先移除再添加
document.querySelector('.zhexian')?.removeAttribute('_echarts_instance_')
document.querySelector(".huan")?.removeAttribute("_echarts_instance_");
var myChart = echarts.init(
document.querySelector(".zhexian") as HTMLElement
)
......
el-icon配合Font awesome字体图标动态使用 血泪教训!!
很多办法如动态组件component :is='' 都渲染不出来
el-menu配置经验
//基本架构
<el-menu>
<template v-for>
<template v-if> //一级栏判断:sub-menu类型
<el-sub-menu>
<el-icon><i class=''>字体图标<el-icon>
<template #title>{{}} </template>
<tempalte> //submenu类型下的二级菜单...
<el-menu-item>
icon
<span>{{}}</span>
</el-menu-item>
</template>
</el-sub-menu>
</tempalte>
<template v-else> //一级栏判断: menu-item类型
<el-menu-item>
icon
<span>{{}}</span>
//用带#title的插槽会出现故障动画,用span会丢失缩进去后的黑框提示
</el-menu-item>
</tempalte>
</el-menu>
弹性盒子下的 justfy-content:right 在微信和360浏览器失效
改用 justify-content: flex-end 兼容性更强
面包屑配置
<el-breadcrumb separator="" v-if="router.currentRoute.value.name == '首页'">
<el-breadcrumb-item
><a
:href="router.currentRoute.value.matched[1].path"
style="font-size: 27px"
>首页</a
></el-breadcrumb-item
>
<el-breadcrumb-item></el-breadcrumb-item>
</el-breadcrumb>
<el-breadcrumb
separator="/"
v-if="router.currentRoute.value.name != '首页'"
>
<el-breadcrumb-item
:to="{ path: router.currentRoute.value.matched[0].path }"
>首页</el-breadcrumb-item
>
<el-breadcrumb-item
><a :href="router.currentRoute.value.matched[1].path">{{
router.currentRoute.value.name
}}</a></el-breadcrumb-item
>
</el-breadcrumb>
reactive更新数组页面未及时渲染
重要用 ref 或者push方法
路由跳转左侧menu不跳转
设置 : default-active =" router . currentRoute . value . path " 属性
跨域:被拒绝的patch请求
简单请求
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight) 。
patch请求属于非简单请求
预检请求
预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。请求头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求的头信息包括两个特殊字段。
Access-Control-Allow-Methods:必选
它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
以前失败的原因:后端未加allow methods