组件库
在项目开发中需要一个组件库快速搭建项目,组件库提供了各式各样的封装完备的组件。现在社区可选择的组件库有 element-plus、antd-vue,Naive-UI、Element3 等。Element3 官网
npm install element3 --savesrc/main.js
import { createApp } from 'vue'
import Element3 from 'element3'
import 'element3/lib/theme-chalk/index.css'
import store from './store/index'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app.use(store).use(router).use(Element3).mount('#app')axios
安装 axios 了(axios 跟 Vue 版本没有直接关系)
npm i axios --save封装网络请求
import axios from 'axios'
import { useMsgbox, Message } from 'element3'
import store from '@/store'
import { getToken } from '@/utils/auth'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
timeout: 5000, // request timeout
})
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['X-Token'] = getToken()
}
return config
},
error => {
console.log(error) // for debug
return Promise.reject(error)
},
)
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 20000) {
console.log('接口信息报错',res.message)
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
console.log('接口信息报错' + error)
return Promise.reject(error)
},
)
export default serviceSass
在项目里集成 CSS 预编译器,CSS 预编译器可以帮我们更快、更高效地管理和编写 CSS 代码
npm install -D sass有了 Sass 之后,我们在 CSS 里使用了变量、嵌套、继承等逻辑,并定义了 white 这两个变量。这样我们就可以嵌套书写 CSS 选择器
<style lang="scss" scoped>
$padding:10px;
$white:#fff;
ul {
width:500px;
margin:0 auto;
padding: 0;
li {
&:hover {
cursor: pointer;
}
list-style-type: none;
margin-bottom: $padding;
padding: $padding;
background: $white;
box-shadow: 1px 3px 5px rgba(0, 0, 0, 0.1);
}
}
</style>至此基于 Vite + Vue 3 + Vue Router + Vuex + Element3 + Axios + Sass 的前端项目开发环境搭建完毕
代码规范和提交规范
进入到项目文件夹安装 ESLint
npm i eslint -D
# 初始化 eslint
npx eslint --initESLint 安装成功后,在项目根目录下执行 npx eslint --init,然后按照终端操作的提示完成一系列设置来创建配置文件

开发权限系统
角色权限
通常使用的权限解决方案就是 RBAC 权限管理机制。每个用户有不同的角色,每个角色对应不同的页面权限,这个数据结构的关系设计主要是由后端来实现。
这样有一部分页面是写在代码的 src/router/index.js 中,另外一部分页面通过 axios 获取数据后,通过调用 vue-router 的 addRoute 方法动态添加进项目整体的路由配置中
在 Vuex 中注册 addRoute 这个 action,通过后端返回的权限页面数据,调用 router.addRoute 新增路由
addRoutes({ commit }, accessRoutes) {
// 添加动态路由,同时保存移除函数,将来如果需要重置路由可以用到它们
const removeRoutes = []
accessRoutes.forEach(route => {
const removeRoute = router.addRoute(route)
removeRoutes.push(removeRoute)
})
commit('SET_REMOVE_ROUTES', removeRoutes)
},与新增路由对应,在页面重新设置权限的时候,需要用 router.removeRoute来删除注册的路由,需要把动态路由的状态存储在本地存储里,否则刷新页面之后,动态的路由部分就会被清空,页面就会显示 404 报错。
需要在 localStorage 中把静态路由和动态路由分开对待,在页面刷新的时候,通过 src/router/index.js 入口文件中的 routes 配置,从 localStorage 中获取完整的路由信息,并且新增到 vue-router 中,才能加载完整的路由
在常见的登录状态下 token 过期,即使依然存在 token,刷新页面后也会跳转到登录页。登录状态过期,有一个单独的报错信息,需要在接口拦截函数中,统一对接口的响应结果进行拦截。如果报错信息显示的是登录过期,需要清理所有的 token 和页面权限数据,并且跳转到登录页面
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012) {
// to re-login
Msgbox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning',
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))Vue3 集成第三方框架
nprogress
npm install nprogress -D需要使用 NProgress 的地方进行 import 的相关操作,比如在页面跳转的时候,就需要使用 NProgress 作为进度条。导入 NProgress 库之后,我们就不需要使用 Vue3 的插件机制进行注册
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
router.beforeEach(async (to, from, next) => {
// start progress bar
NProgress.start()
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})组件的封装
以可视化组件为例,来分析复杂组件的封装。用可视化框架 ECharts 去封装 Vue 的组件,来实现可视化组件。不管你选择用百度的 ECharts,还是蚂蚁的 G2 等框架,在框架的使用方法上,都是类似的。首先,你需要完成图表库的配置,并且填入图表数据,然后把这个数据渲染在一个 DOM 上就可以了
<template>
<div ref="chartRef" class="chart"></div>
</template>
<script setup>
import * as echarts from 'echarts'
import {ref,onMounted,onUnmounted} from 'vue'
// 通过ref获得DOM
let chartRef = ref()
let myChart
onUnmounted(()=>{
myChart.dispose()
myChart = null
})
onMounted(()=>{
myChart = echarts.init(chartRef.value)
const option = {
tooltip: {
trigger: 'item'
},
color: ['#ffd666', '#ffa39e', '#409EFF'],
series: [
{
name: '前端课程',
type: 'pie',
radius: '70%',
data: [
{value: 43340, name: '重学前端'},
{value: 7003, name: 'Javascript核心原理解析'},
{value: 4314, name: '玩转Vue3全家桶'}
]
}
]
}
myChart.setOption(option)
})
</script>ECharts 是一个非常复杂的可视化框架,有饼图、地图等不同的图表类型,如果引入 ECharts 全部代码的话,项目的体积会变得非常臃肿。要按需引入
指令的封装
在 Vue 中注册指令和组件略有不同,注册实现了 v-focus 指令,然后在 input 标签中加上 v-focus 指令
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
// 当被绑定的元素挂载到 DOM 中时……
mounted(el) {
// 聚焦元素
el.focus()
}
})指令的生命周期和组件类似,首先要让指令能够支持 Vue 的插件机制,需要在 install 函数内注册 lazy 指令,并且实现了 mounted、updated、unmounted 三个钩子函数
const lazyPlugin = {
install (app, options) {
app.directive('lazy', {
mounted: ...,
updated: ...,
unmounted: ...
})
}
}通过 lazy 指令获取到当前图片的标签,并且计算图片的位置信息,判断图片是否在首页显示。如果不在首页的话,图片就加载一个默认的占位符就可以了,并且在页面发生变化的时候,重新进行计算,这样就实现了页面图片的懒加载
与懒加载类似的,还有我们组件库中常用的 v-loading 指令,它用来显示组件内部的加载状态下面的代码中,我们注册了 loadingDirective 指令,并且注册了 mounted、updated、unmounted 三个钩子函数,通过 v-loading 的值来对显示效果进行切换,实现了组件内部的 loading 状态
动态切换的 Loading 组件能够显示一个 circle 的 div 标签,通过 v-loading 指令的注册,在后续表格、表单等组件的提交状态中,加载状态就可以很方便地使用 v-loading 来实现
const loadingDirective = {
mounted: function (el, binding, vnode) {
const mask = createComponent(Loading, {
...options,
onAfterLeave() {
el.domVisible = false
const target =
binding.modifiers.fullscreen || binding.modifiers.body? document.body: el
removeClass(target, 'el-loading-parent--relative')
removeClass(target, 'el-loading-parent--hidden')
}
})
el.options = options
el.instance = mask.proxy
el.mask = mask.proxy.$el
el.maskStyle = {}
binding.value && toggleLoading(el, binding)
},
updated: function (el, binding) {
el.instance.setText(el.getAttribute('element-loading-text'))
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding)
}
},
unmounted: function () {
el.instance && el.instance.close()
}
}
export default {
install(app) {
// if (Vue.prototype.$isServer) return
app.directive('loading', loadingDirective)
}
}引入第三方库的注意事项
封装第三方库的目的是实现第三方框架和 Vue 框架的融合,提高开发效率。引入第三方库相关的注意事项。
-
无论是引用第三方库还是自己封装的底层库,要考虑到项目的长期可维护性;其次,尽可能不要因为排期等问题,一股脑地把第三方库堆在一起,虽然这样做可以让项目在早期研发进度上走得很快,但这样会导致项目中后期的维护成本远远大于重写一遍代码 xxx 的成本
-
然后是 Vue 中的 mixin,extends 机制能不用就不用,这两个 API 算是从 Vue 2 时代继承下来的产物,都是扩展和丰富 Vue 2 中 this 关键字,在项目复杂了之后,mixin 和 extends 隐式添加的 API 无从溯源,一旦多个 mixin 有了命名冲突,调试起来难度倍增
-
项目中的全局属性也尽可能少用,全局变量是最原始的共享数据的方法,Vue 3 中我们使用
app.config.globalProperties.x注册全局变量,要少用它的主要原因也是项目中的全局变量会极大的提高维护成本。有些监控场景必须要用到,就要把所有注册的全局变量放在一个独立的文件去管理