小程序开发|小程序制作|小程序开发网

搜索

使用vite搭建基于vue3.0+element-plus的后台管理系统

2021-12-14 21:00| 发布者: 牛摩的| 查看: 187| 评论: 1

摘要: 最近vite越来火爆了,2.0发布以后也趋于稳定,写了一个demo发现运行速度确实非常快,和使用webpack的vue-cli相比完全是降维打击,想着搭个架子,方便以后使用。小菜鸡的初次尝试,希望各位大神不吝赐教。1、使用yarn
最近vite越来火爆了,2.0发布以后也趋于稳定,写了一个demo发现运行速度确实非常快,和使用webpack的vue-cli相比完全是降维打击,想着搭个架子,方便以后使用。小菜鸡的初次尝试,希望各位大神不吝赐教。

1、使用yarn初始化vite模板

yarn create @vitejs/app
之后会让你输入项目名称,选择框架等。这里我们输入名称为jianshu,框架选择vue,回车
然后进入项目中,输入yarn回车,安装依赖
cd jianshu && yarn
安装完成之后,使用yarn dev命令启动开发服务。
可以看到只花了477毫秒就启动完成了,对比vue-cli来说快了很多。我们看到vite默认端口是3000,而不是8080,这个可以在项目里配置。打开localhost:3000地址,可以看到vite默认的欢迎页面。

2、安装我们需要用到的插件

网络请求使用axios,css预处理使用sass,还有登录验证会用到的jd-md5加密,当然还有我们的element-plus,最后就是vue全家桶:vuex和vue-router。
yarn add axios sass js-md5 element-plus

由于默认安装vuex和vue-router还是3.x,和vue3.0搭配不上,这里我们单独安装这两个插件的最新的4.x版本

yarn add vuex@lastest
yarn add vue-router@lastest
全部安装完成后,来进行配置

3、插件的配置

①配置vuex

vuex4.x的语法和3.x差距比较大,有能力的同学可以自行翻看官方文档。在src目录下新建store文件夹,然后新建index.js。
这里我们只配置最基础的token,userInfo,storeInfo三个数据,全部存入localstorage中做持久化。
然后配置了set方法来设置/清空数据。集成了loginIn和loginOut的方法,方便调用。
import { createStore } from 'vuex';export default createStore({  state() {    return {      userInfo: JSON.parse(localStorage.getItem('userInfo')),      storeInfo: JSON.parse(localStorage.getItem('storeInfo')),      token: localStorage.getItem('token')    }  },  mutations: {    SetToken(state, data) {      state.token = data.token;      if(data){        localStorage.setItem('token', data.token);        localStorage.setItem('expire', data.expire)      }else{        localStorage.removeItem('token');        localStorage.removeItem('expire');      }    },    SetUser(state, data) {      state.userInfo = data;      if(data){        localStorage.setItem('userInfo', JSON.stringify(data));      }else{        localStorage.removeItem('userInfo');      }    },    SetStore(state, data){      state.storeInfo = data;      if(data){        localStorage.setItem('storeInfo', JSON.stringify(data));      }else{        localStorage.removeItem('storeInfo');      }    },    LoginIn(state, data) {      this.commit('SetToken', data);      this.commit('SetUser', data.user);    },    LoginOut() {      this.commit('SetToken', '');      this.commit('SetUser', '');      this.commit('SetStore', '');      location.href = '/';    },  }});
然后在main.js里进行配置。默认的main.js是这样的
import { createApp } from 'vue'import App from './App.vue'createApp(App).mount('#app')
不太方便我们配置插件,这里需要改造一下
import { createApp } from 'vue'import App from './App.vue'const app = createApp(App);app.mount('#app');
引入store
import store from './store';...app.use(store);

②配置网络请求axios

在src目录下新建plugins文件夹,然后新建axios.js。
引入axios和element-plus的loading组件,然后引入store。
import axios from 'axios';import {ElLoading} from 'element-plus';import store from '../store';import {uiMsg, apiHost} from '../utils';
这里的uiMsg和apiHost是单独封装到utils里面,方便调用的
在src目录下新建utils文件夹,然后新建index.js,封装一些常用的工具函数。
import { ElMessage } from 'element-plus';// api// const apiHost = 'https://product.com/api'; //生产const apiHost = 'http://dev.com/api'; //开发// 消息提示function uiMsg(msg, type = 'error', onClose) {  ElMessage.closeAll();  ElMessage({    message: msg,    type,    duration: 1500,    customClass: 'ui-msg-zindex',    onClose: () => {      onClose && onClose();    }  });}// 复制剪贴板function uiCopy(str) {  let copyDom = document.createElement('div');  copyDom.innerText = str;  copyDom.style.position = 'absolute';  copyDom.style.top = '0px';  copyDom.style.right = '-9999px';  document.body.appendChild(copyDom);  //创建选中范围  let range = document.createRange();  range.selectNode(copyDom);  //移除剪切板中内容  window.getSelection().removeAllRanges();  //添加新的内容到剪切板  window.getSelection().addRange(range);  //复制  let successful = document.execCommand('copy');  copyDom.parentNode.removeChild(copyDom);  try {    uiMsg({      msg: successful ? "复制成功!" : "复制失败,请手动复制内容",      type: successful ? 'success' : 'error'    })  } catch (err) {}}// 深拷贝function uiDeepCopy(obj, cache = []) {  function find(list, f) {      return list.filter(f)[0];  }  if (obj === null || typeof obj !== 'object') {      return obj;  }  const hit = find(cache, (c) => c.original === obj);  if (hit) {      return hit.copy;  }  const copy = Array.isArray(obj) ? [] : {};  cache.push({original: obj, copy});  Object.keys(obj).forEach((key) => {      copy[key] = uiDeepCopy(obj[key], cache);  });  return copy;}export { apiHost, uiMsg, uiCopy, uiDeepCopy }
初始化axios,使用requestCount来优化多个请求同步进行时,loading闪烁的问题。
let http = axios.create({  baseURL: apiHost,  timeout: 6000,  headers: {    'Content-Type': 'application/json;charset=UTF-8;'  }});let loading,  requestCount = 0;const ShowLoading = ()=>{  if(requestCount === 0 && !loading){    loading = ElLoading.service({      background: 'rgba(0,0,0,.7)'    });  }  requestCount++;}const HideLoading = ()=>{  requestCount--;  if(requestCount === 0){    loading.close();  }}
因为我们后台接口大部分都是使用jwt认证,所以需要在request拦截器中给请求加上token。
然后在response拦截器中加入token过期失效的处理,这里可以根据实际情况修改判断条件。
http.interceptors.request.use(config=>{  ShowLoading();  if (store.state.token) {    config.headers['Authorization'] = 'Bearer ' + store.state.token;  }  config.headers.post['Content-Type'] = 'application/json';  return config;});http.interceptors.response.use(response=>{  HideLoading();  return response.data;}, error=>{  if(error.response.status === 401){    // 401 token过期,退出登录    uiMsg('登录已过期,请重新登录', null, ()=>{      store.dispatch('LoginOut');    });  }else{    return Promise.reject(error)  }});
封装get和post请求。加入函数节流处理,添加接口状态判断和错误消息提示。
/** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ function get(url, params) {  return new Promise((resolve) => {    let _timestamp = new Date().getTime();    http.get(url, { params }).then(res => {      if(new Date().getTime() - _timestamp < 200){        setTimeout(() => {          if (res.code === 200) {            resolve(res);          } else {            res.msg && uiMsg(res.msg);          }        }, 200);      }else{        if (res.code === 200) {          resolve(res);        } else {          res.msg && uiMsg(res.msg);        }      }    }).catch(error => {});  })}/** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ function post(url, params) {  return new Promise((resolve) => {    let _timestamp = new Date().getTime();    http.post(url, params).then(res => {      if(new Date().getTime() - _timestamp < 200){        setTimeout(() => {          if (res.code === 200) {            resolve(res);          } else {            res.msg && uiMsg(res.msg);          }        }, 200);      }else{        if (res.code === 200) {          resolve(res);        } else {          res.msg && uiMsg(res.msg);        }      }    })    .catch(error => {});  })}export {get, post};
完成后在main.js中进行配置,同时引入utils里面的工具函数,挂载到vue全局上。
import {get, post} from './plugins/axios';import {uiMsg, uiCopy, uiDeepCopy} from './utils';...app.config.globalProperties.$uiMsg = uiMsg;app.config.globalProperties.$uiCopy = uiCopy;app.config.globalProperties.$uiDeepCopy = uiDeepCopy;app.config.globalProperties.$get = get;app.config.globalProperties.$post = post;

③配置vue-router

在scr目录下新建router目录,然后新建index.js。
引入vue-router和vuex。
import {createRouter, createWebHistory} from 'vue-router';import store from '../store';
首先配置不需要权限控制的页面。在src目录下新建views目录,然后分别新建登录:/login/index.vue,404:/error/notFound.vue,无权限:/error/noPermission.vue页面。这里我们使用异步加载引入。
// 不需要权限的页面const constantRoutes = [  {    // 登录    path: '/login',    name: 'login',    component: ()=>import('../views/login/index.vue')  },  {    // 404    path: '/:pathMatch(.*)',    name: 'notFound',    component: ()=>import('../views/error/notFound.vue')  },  {    // 无权限    path: '/noPermission',    name: 'noPermission',    component: ()=>import('../views/error/noPermission.vue')  }];
然后来配置普通页面。随意配置三个示例页面。
const asyncRoutes = {  path: '/',  name: 'main',  component: ()=>import('../views/main.vue'),  children: [    {      // 首页      path: '/',      name: 'home',      component: ()=>import('../views/home/index.vue')    },    {      // 用户列表      path: '/userList',      name: 'userList',      component: ()=>import('../views/setting/userList.vue')    },    {      // 权限设置      path: '/permission',      name: 'permission',      component: ()=>import('../views/setting/permission.vue')    }  ]}
然后来初始化router。
const router = createRouter({  history: createWebHistory('/'),  routes: constantRoutes});router.addRoute(asyncRoutes);
我们在router跳转中,加入token判断
router.beforeEach((to, from, next)=>{  // 登录判断  if(store.state.token){    if(to.path === '/login'){      next({path: '/'});    }else{      // 权限判断      next();    }  }else{    if(to.path === '/login'){      next();    }else{      next({path: '/login'})    }  }});
跳转完成后,将滚动条位置重置。
router.afterEach(to => {  window.scrollTo(0, 0);});
最后导出router
export default router;
然后到main.js中配置
import router from './router';...app.use(router);

④配置elemet-plus。

在plugins文件夹下新建element.js。
import ElementPlus from 'element-plus';import 'element-plus/dist/index.css';export default app=>{  app.use(ElementPlus);}
然后在main.js中进行配置。
import installElementPlus from './plugins/element';...installElementPlus(app);

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

鲜花

握手

雷人

路过

鸡蛋
发表评论

最新评论

引用 北极圈的瓜 2021-12-14 21:10
const asyncRoutes = { path: '/', name: 'main', component: ()=>import('../views/main.vue'),这个main 是什么 报错了

查看全部评论(1)

返回顶部