组件的封装
在使用 vue-cli 创建的项目中,组件的创建非常方便,只需要新建一个 .vue 文件,然后在 template 中写好 HTML 代码,一个简单的组件就完成了。一个完整的组件,除了 template 以外,还有 script和 style
props参数(父对子传参)
slot定制插槽
event自定义事件
接收方式为标题需要用户传入title属性,图标和显示内容是需要传入元素数据的,我们使用vue中的插槽进行封装,得出来的结果是
<template>
<div
class="w-full h-[200]px bg-white rounded-md shadow-sm p-5 m-4 transition-shadow duration-700 hover:shadow-lg"
>
<div class="flex place-content-between align-center">
<div class="text-xl">{{ props.title }}</div>
<div>
<slot name="icon"></slot>
</div>
</div>
<el-divider></el-divider>
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{ title: string }>()
</script>
当所有的组件封装好了之后,我们只需要通过import方式导入对应的组件并插入到template中即可。(别忘记传入数据哦)
axios的封装
在vue项目中,和后台进行请求交互这块,我们通常都会选择axios库,它是基于promise的http库,可运行在浏览器端和node.js中。在本项目中主要实现了请求和响应拦截,get,post请求封装。
import axios from 'axios'
import Qs from 'qs'
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
import { Loading, Message } from 'element-ui'
const $axios = axios.create({
timeout: 30000,
baseURL: process.env.VUE_APP_BASE_API
})
Vue.prototype.$http = axios
let loading = null
$axios.interceptors.request.use(
config => {
loading = Loading.service({ text: '拼命加载中' })
const token = store.getters.token
if (token) {
config.headers.Authorization = token
}
return config
},
error => {
return Promise.reject(error)
}
)
$axios.interceptors.response.use(
response => {
if (loading) {
loading.close()
}
const code = response.status
if ((code >= 200 && code < 300) || code === 304) {
return Promise.resolve(response.data)
} else {
return Promise.reject(response)
}
},
error => {
if (loading) {
loading.close()
}
console.log(error)
if (error.response) {
switch (error.response.status) {
case 401:
store.commit('DEL_TOKEN')
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break
case 404:
Message.error('网络请求不存在')
break
default:
Message.error(error.response.data.message)
}
} else {
if (error.message.includes('timeout')) {
Message.error('请求超时!请检查网络是否正常')
} else {
Message.error('请求失败,请检查网络是否已连接')
}
}
return Promise.reject(error)
}
)
export default {
post(url, data) {
return $axios({
method: 'post',
url,
data: Qs.stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
})
},
get(url, params) {
return $axios({
method: 'get',
url,
params
})
}
}
用户权限和动态路由(vue-element-admin)
前端在本地写好路由表,以及每个路由对应的角色,也就是哪些角色可以看到这个菜单 / 路由。
登录的时候,向后端请求得到登录用户的角色(管理者,普通用户)
利用路由导航守卫(router.beforeEach(全局前置守卫 进入路由之前)),根据取到的用户角色,跟本地的路由表进行对比,过滤出用户对应的路由,并利用路由进行菜单渲染
每次更改页面路由
你有没有token啊?
有的
好的,你的权限是默认的权限0么?
是的。。我就是一游客
系统获取我的信息..拿到权限值,动态加载路由(GenerateRoutes)...通行...
不是。。我是权限汪(admin)
等等..我看看作者有没有把你降级
没有
好了。。你还是权限汪 请进
有
滚吧,你已经不是权限汪了,作者已经把你写成战斗力只有5的渣渣了
没有
没有还敢闯这里?滚去关口(/login)
我们将储存在将storage中的token作为用户是否登录的标志,如果当前storage中有token,表明当前系统已被登录
将系统所有页面分为两类,需要登录才能查看的页面,不需要登录的login.vue, register.vue等
动态路由
前端将全部路由规定好,登录时根据用户角色权限来动态展示路由;
路由是组织一个vue项目的关键,在对项目原型分析后,接下来的第一步就是编写路由,本项目中,主要分为两种路由,currencyRoutes 和 asyncRoutes
currencyRoutes:代表通用路由,意思就是不需要权限判断,不同角色用户都显示的页面,如:登陆页、404等
asyncRoutes: 代表动态路由,需要通过判断权限动态分配的页面,有关的权限判断的方法接下来会介绍。
项目优化
路由懒加载
传统的路由组件是通过import静态的打包到项目中,这样做的缺点是因为所有的页面组件都打包在同一个脚本文件中,导致生产环境下首屏因为加载的代码量太多会有明显的卡顿(白屏)
通过import()使得ES6的模块有了动态加载的能力,让url匹配到相应的路径时,会动态加载页面组件,这样首屏的代码量会大幅减少,webpack会把动态加载的页面组件分离成单独的一个chunk.js文件
预渲染
由于浏览器在渲染出页面之前,需要先加载和解析相应的html,css和js文件,为此会有一段白屏的时间,如何尽可能的减少白屏对用户的影响,目前我选择的是在html模版中,注入一个loading动画,这里我拿D2-Admin中的loading动画举例
在打包完成后,在这个index.html下方还会注入页面的脚本,当用户访问你的项目时,脚本还没有执行,但是可以显示loading动画,因为它是直接注入在html中的,等到脚本执行完毕后,Vue会新生成一个app的节点然后将旧的同名节点删除,这样可以有效的过渡白屏的时间
loading动画只是一个让用户感知到你程序正在启动的效果,只是一个静态页面没有任何的功能
element-ui按需加载的两种方式
1.直接按需引入组件及样式
import { Dialog } from 'element-ui'
import 'element-ui/lib/theme-chalk/dialog.css'
Vue.use(Dialog)
2.使用babel-plugin-component按需引入
cdn引入
对于一些不常改动的模块库,例如: vue vueRouter vuex echarts element-ui 等, 我们让 webpack 不将他们进行打包,而是通过 cdn 引入,这样就可以减少代码大小,减少服务器带宽,并通过cdn将它们缓存起来,提高网站性能 。