0.Vue3项目的一些思考
做完了B站上的前端项目的一些笔记和思考。根据我认为一个前端项目的组成重点编写。项目位置:通用后台管理系统前端Vue3: Vue3+ElementPlus+axios+vuex+mock+vueRouter (gitee.com)
1.界面样子部分
界面的样子主要是用ElementPlus等UI组件库结合HTML5的相关语法来构成的,所以此处的关键是要熟练的引用UI组件库,基本上不存在难的逻辑难点,孰能生巧。
界面的样式主要是通过CSS来调整的,此处的关键除了记住几个常用的样式属性之外就是要学会在游览器上进行直接的调试,基本上也没有逻辑难点。
1.1ElemntPlus等UI组件库
Vue2.x的版本使用ElementUI,而Vue3.x的部分使用ElementPlus,如果版本使用错误则项目会无法运行。关于这样的组件库有两个比较重要的部分。
第一个是要在官网上明确不同版本不同时间上官网给出的不同导入项目的方式,包括全导入、自动导入、按需导入等,所以重点之一是看文档的能力。
第二个就是要找到对应的组件并将其导入项目,关键是看懂各个组件的代码并会修改,不难。
1.2 CSS/less等样式的设计
样式的设计同样是一个孰能生巧的部分,关键是学会在游览器上调试。less是CSS的一个加强版,让样式在
"less": "^4.1.3",
"less-loader": "^11.1.0", //好像时less的解析器
在该部分学会了一种样式重置的方法。首先建立如下的less包结构:
在reset.less当中写入自己重置的样式代码。在index.less中加入代码:
@import "./reset.less";
随后在main.js当中完成导入。
import './assets/less/index.less'
2.VueRouter的路由部分
2.1简单路由设置
一般相关代码写在src/router/index.js目录下,代码如下:
import {createRouter,createWebHashHistory} from 'vue-router' //导入两个相关组件
const routes = [
{
path:'/', //主路由
component:()=>import('../views/Main.vue'), //相应的组件在文件中的位置
name:'home1', //该条路由的标识,类似于key
redirect:'/home', //该路由的重定向位置
// children:[ //代表该主路由下的子路由属性设置
// {
// path: '/home',
// name: 'home',
// component: () => import('../views/home/Home.vue')
// },
// {
// path: '/user',
// name: 'user',
// component: () => import('../views/User/User.vue')
// },
// {
// path: '/page1',
// name: 'page1',
// component: () => import('../views/Page1.vue')
// },
// {
// path: '/page2',
// name: 'page2',
// component: () => import('../views/Page2.vue')
// }
// ]
children:[], //动态路由时使用
},
{
path:'/login',
name:'login',
component:()=>import ("../views/Login.vue"),
},
]
const router = createRouter({ //在组件内写入相应的参数后,利用组件创建路由
history: createWebHashHistory(),
routes //上方定义的
})
export default router //向外暴露路由
2.2动态路由设计
如果我们的路由信息并不是直接写死的,而是通过数据库等动态给出,那么就需要进行动态路由设计。
首先查看动态路由的导入位置,写在main.js部分:
//省略了部分代码
import router from "./router/index.js";
import store from "./store/index.js"; //Vuex相关部分,理解为缓存
store.commit('addMenu',router); //调用缓存store文件中的addMenu方法,给的参数是router
接下里进入./store/index.js查看相关的代码,如下:
addMenu(state,router){ //addMenu方法,本文件内部参数state,外部输入参数router
if (!localStorage.getItem('menu')){
return
}
//setmenu方法在Login.Vue中,即登录页面
//从localStorage获取存有路由信息(其它接口获取)的menu并放在文件store缓存的state.menu位置
const menu = JSON.parse(localStorage.getItem('menu'))
state.menu = menu;
//动态路由
const menuArray =[];
menu.forEach(item =>{
if (item.children){ //有子路由的路由
item.children =item.children.map(item =>{ //处理children
let url = `../views/${item.url}.vue`; //要连接的组件的位置
// console.log(url);
item.component = ()=>import(url);
return item;
})
menuArray.push(...item.children);
}else { //没有子路由
let url = `../views/${item.url}.vue`;
// console.log(url);
item.component = ()=>import(url);
menuArray.push(item);
}
})
menuArray.forEach(item =>{
router.addRoute('home1',item); //调用router的方法加入这些路由
})
},
2.3路由导航守卫与token
token存放在Cookie当中,相关代码方法存放在./store/index.js当中,较好理解:
setToken(state,val){
state.token =val;
Cookies.set('token',val);
},
clearToken(state){
state.token ='';
Cookies.remove('token');
},
getToken(state){
state.token = state.token || Cookies.get("token");
}
关键是路由导航守卫的代码,写在main.js当中,因为守卫是全局使用的。
//路由守卫,此处的注释相当于没有注释
// function checkRouter(path){ //自定义函数,path是外部传入参数,应该是当前的path
// let hasCheck = router.getRoutes().filter(route =>{ //与所有路由一一比较
// route.path === path;
// }).length; //取其长度
// if (hasCheck){
// return true
// }else {
// return false //即此位置不在已注册的路由当中
// }
// }
router.beforeEach((to,from,next) =>{ //to:要去的位置(路由),from:来自的路由,next:中间过程
store.commit('getToken')
const token = store.state.token
if (!token && to.name !== 'login'){ //token不存在且目标路径不是登录页面
console.log("运行到了这里");
next({name:'login'});
}
// else if (!checkRouter(to.path)){ //请求的路径不在已注册的路径当中
// next({name:'home'}) //中途转发至name为home的路由
// }
else {
next(); //中间过程全部放行
}
})
3.组件通信与缓存
3.1Vuex与js-cookie
js-cookie主要用于Cookie相关操作,理解为往Cookie当中进行增删改查。Vuex可以理解为数据的缓存,但是当网页刷新的时候内部数据会消失,所以可以往Cookie当中写入一部分的重要数据,做一个简单的数据持久化,比如说token。由于Vuex的特性(存储),所以可以实现各组件之间的交互。
export default createStore({
state:{ //可以理解为缓存当中的键值对信息以及数据初始值。
isCollapse:true, //如建名为iscollapse的数据初始值为true
currentMenu:null,
tabsList:[
{
path:'/',
name:'home',
label:'首页',
icon:'home'
}
],
menu:[],
token:null,
},
mutations:{ //内部写入相应的方法
}
4.与后端的接口连接
4.1 axios的简单设置
axios是接口,用来定义与后端的网络连接,也就是接口的定义。通常在后端的接口未给出的时候与mock结合使用,给出后直接与后端进行连接。
首先看一下包结构:
与axios相关的主要是两个,也就是api.js和request.js,其中的关键是request.js。首先看一下api.js,里面定义了接口的具体地址。
//对整个项目的api管理
import request from "./request.js";
export default {
getTableData(params){ //其它组件对接口的使用这主要通过此处定义的函数,如getTableData,params为参数
return request({
// url:'/home/getTableData',
url:'/home/getData', //对外连接的http位置,request.js当中axios的baseurl
method:'get',
data:params,
mock:true
})
},
getCountData(){
return request({
url:'/home/getCountData',
method:'get',
mock:true
})
},
getUserData(Params){
return request({
url:'/user/getUser',
method:'get',
data:Params,
mock:false
})
},
getMenu(Params){
return request({
url:'permission/getMeun',
method:'post',
mock:false,
data:Params
})
}
}
然后是有关于request.js的部分:
//创建一个axios实例对象,一切的起始
const servie = axios.create({
baseURL:config.baseApi //基础http位置,也就是共同的http头
})
//在前往请求之前做一些事情
servie.interceptors.request.use((req)=>{
//可以自定义header
//jwt-token认证的时候
return req;
})
//在请求结束后做一些事情
servie.interceptors.response.use((req)=>{
const {code,data,msg} = req.data
//根据后端 协商 视情况而定
if (code === 200){
return data;
}else {
//网络错误
ElMessage.error(msg||NETWORK_ERROR);
return Promise.reject( msg||NETWORK_ERROR);
}
})
//上面可以理解为配置,具体axios使用是调用其实例的方法,如servie(options)
4.2 axios与mock结合的二次封装
主要是在request.js当中具体定义了一个函数。
import config from "../config/index.js";
//封装的核心函数
function request(options){
options.method = options.method || 'get'
if(options.method.toLowerCase()==='get'){
options.params =options.data
}
//对mock的处理
let isMock =config.mock
if (typeof options.mock !== 'undefined'){
isMock = options.mock;
}
//对线上环境做处理
if (config.env ==="prod"){
//不给你用到mock的机会
servie.defaults.baseURL = config.baseApi
}else{
servie.defaults.baseURL = isMock ? config.mockApi : config.baseApi
}
return servie(options) //调用axios的实例
}
export default request
其中config的代码如下:
//当前的环境,默认prod线上环境
const env = import.meta.env.MODE || 'prod'
const EnvConfig ={
development:{
baseApi:'/',
mockApi:'/',
},
test:{ //测试环境
baseApi:'/api',
mockApi:'/api',
},
pro:{ //线上环境
baseApi:'/api',
mockApi:'/api',
},
}
export default {
env,
mock:true,
...EnvConfig[env]
}
对于总体的其它组件的axios调用以Login.vue为例子:
const {proxy} = getCurrentInstance();
const login =async ()=>{
const res = await proxy.$api.getMenu(loginForm) //理解为调用api.js当中的getMenu方法
}
5.前端数据的模拟
利用mock进行数据的模拟。
5.1mock的简单原理与数据放置位置
首先在src/mockData/xx.js下放入自己的数据,如:
export default {
getUserList: () => {
return {
code: 200,
data: {
tableData: [
{
age:18,
name:"jj",
},
{
age: 112,
name:"sadcf",
},
],
}
}
}
}
接下里理解一下mock的本质就是拦截本该发给真后端的http请求,然后代给出自己的假数据。我们将拦截代码放在src/api/mock.js当中。
import Mock from 'mockjs'
import homeApi from './mockData/home'
import userApi from './mockData/user1'
import permsiionApi from './mockData/permission'
//拦截请求
Mock.mock('/home/getData',homeApi.getHomeData)
Mock.mock('/home/getCountData',homeApi.getCountData)
// Mock.mock(/user\/getUser/,'get',userApi.getUserList)
Mock.mock('/user/getUser',userApi.getUserList)
Mock.mock('/permission/getMeun',permsiionApi.getMenu)
5.2线上的mock
网上搜索fastmock,不过不是很好用。