|
|
|
|
# 15 | 实战痛点1:复杂Vue项目的规范和基础库封装
|
|
|
|
|
|
|
|
|
|
你好,我是大圣,欢迎进入课程的第15讲。
|
|
|
|
|
|
|
|
|
|
在全家桶实战篇的前几讲里,我们学习了Vue 3本身的进阶内容。从今天开始,我们尝试着把这些技能都用在实际项目中,聊一下实战中常见的痛点。不过,既然是实际项目,那还是有很多库需要引入的,比如网络请求时用到的axios、时间处理时用到的Dayjs等等。今天我要跟你聊的,则是复杂 Vue 项目的规范和基础库的封装。
|
|
|
|
|
|
|
|
|
|
## 组件库
|
|
|
|
|
|
|
|
|
|
在项目开发中,我们首先需要一个组件库帮助我们快速搭建项目,组件库提供了各式各样的封装完备的组件。现在社区可选择的组件库有element-plus、antd-vue,Naive-UI、Element3等,我们选择Element3来搭建项目,首先我们来到项目目录下,执行下面的代码安装Element3。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install element3 --save
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
然后,我们在src/main.js中使用一下Element3。看下面的代码,我们在其中引入了Element3和主体对应的CSS,并使用use(Element3)加载组件库。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这样,项目的入口页面就注册好了Element3内置的组件。关于Element3的组件列表,你可以到[Element3官网](https://e3.shengxinjing.cn/#/component/installation)查看。接下来,我们就可以使用组件库去搭建我们的页面了。
|
|
|
|
|
|
|
|
|
|
首先,我们打开项目目录下的src/App.vue文件,把之前的学习清单应用时的测试代码移除,然后新增下面的代码。你能看到,我们在代码中使用Element3的[Container布局组件](https://e3.shengxinjing.cn/#/component/container)实现管理系统的整体布局。
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<template>
|
|
|
|
|
|
|
|
|
|
<el-container>
|
|
|
|
|
<el-header>Header</el-header>
|
|
|
|
|
<el-container>
|
|
|
|
|
<el-aside width="200px">
|
|
|
|
|
<div>
|
|
|
|
|
<router-link to="/"> Home</router-link>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<router-link to="/about">About</router-link>
|
|
|
|
|
</div>
|
|
|
|
|
</el-aside>
|
|
|
|
|
<el-container>
|
|
|
|
|
<el-main>
|
|
|
|
|
<router-view></router-view>
|
|
|
|
|
</el-main>
|
|
|
|
|
</el-container>
|
|
|
|
|
</el-container>
|
|
|
|
|
</el-container>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
<style>
|
|
|
|
|
.el-header,
|
|
|
|
|
.el-footer {
|
|
|
|
|
background-color: #b3c0d1;
|
|
|
|
|
color: #333;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-aside {
|
|
|
|
|
background-color: #d3dce6;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-main {
|
|
|
|
|
background-color: #e9eef3;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body > .el-container {
|
|
|
|
|
margin-bottom: 40px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
上面代码对应在前端的显示格局如下,代码上方的Header组件,承载着页面的头部信息,包括项目左上角的名字、右上角的用户信息、消息等等。代码中的aside对应了前端页面左侧的侧边栏,承载着页面主要的导航信息;main组件内部使用router-view渲染路由对应的组件,然后我们继续使用Element3逐渐丰富Header和导航信息。
|
|
|
|
|
|
|
|
|
|
## ![图片](https://static001.geekbang.org/resource/image/d2/2a/d237cyycfb6d9c10aa7a04dc5073852a.png?wh=1920x539)
|
|
|
|
|
|
|
|
|
|
在Element3中,我们也可以很方便地找出我们需要的组件,就像Menu等等。在下面的代码中,我们使用el-menu组件渲染header组件,el-menu内部使用el-ment-item渲染导航组件。
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<el-header>
|
|
|
|
|
<el-menu
|
|
|
|
|
:default-active="1"
|
|
|
|
|
class="el-menu-demo"
|
|
|
|
|
mode="horizontal"
|
|
|
|
|
background-color="#545c64"
|
|
|
|
|
text-color="#fff"
|
|
|
|
|
active-text-color="#ffd04b"
|
|
|
|
|
>
|
|
|
|
|
<el-menu-item index="1">处理中心</el-menu-item>
|
|
|
|
|
<el-submenu index="2">
|
|
|
|
|
<template v-slot:title>我的工作台</template>
|
|
|
|
|
<el-menu-item index="2-1">选项1</el-menu-item>
|
|
|
|
|
<el-menu-item index="2-2">选项2</el-menu-item>
|
|
|
|
|
<el-menu-item index="2-3">选项3</el-menu-item>
|
|
|
|
|
<el-submenu index="2-4">
|
|
|
|
|
<template v-slot:title>选项4</template>
|
|
|
|
|
<el-menu-item index="2-4-1">选项1</el-menu-item>
|
|
|
|
|
<el-menu-item index="2-4-2">选项2</el-menu-item>
|
|
|
|
|
<el-menu-item index="2-4-3">选项3</el-menu-item>
|
|
|
|
|
</el-submenu>
|
|
|
|
|
</el-submenu>
|
|
|
|
|
<el-menu-item index="3" disabled>消息中心</el-menu-item>
|
|
|
|
|
<el-menu-item index="4"
|
|
|
|
|
><a href="https://element3-ui.com" target="_blank"
|
|
|
|
|
>订单管理</a
|
|
|
|
|
></el-menu-item
|
|
|
|
|
>
|
|
|
|
|
</el-menu>
|
|
|
|
|
|
|
|
|
|
</el-header>
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
使用menu组件渲染header和aslide组件后,页面布局示意图如下,这样页面的基本结构就搭建完毕了。
|
|
|
|
|
|
|
|
|
|
## ![图片](https://static001.geekbang.org/resource/image/59/7a/59d3d065f3aec7b769341b10da49407a.png?wh=1816x752)
|
|
|
|
|
|
|
|
|
|
## 工具库
|
|
|
|
|
|
|
|
|
|
完成页面基本结构的搭建后,在我们获取后端数据时,需要使用axios发起网络请求。在项目的根目录下,打开命令行,执行下面的命令,这样我们就可以安装axios了(axios 跟 Vue 版本没有直接关系,安装最新即可)。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm i axios --save
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
axios作为现在最流行的网络请求库,可以直接使用axios.get或者axios.post去获取数据。但是在项目开发中,业务逻辑有很多配置需要进行统一设置,**所以安装完axios之后,我们需要做的就是封装项目中的业务逻辑**。
|
|
|
|
|
|
|
|
|
|
首先,在项目在登录成功之后,后端会返回一个token,用来存储用户的加密信息,我们把token放在每一次的http请求的header中,后端在收到请求之后,会对请求header中的token进行认证,然后解密出用户的信息,过期时间,并且查询用户的权限后,校验完毕才会返回对应的数据。
|
|
|
|
|
|
|
|
|
|
所以我们要对所有的http请求进行统一拦截,确保在请求发出之前,从本地存储中获取token,这样就不需要在每个发起请求的组件内去读取本地存储。后端数据如果出错的话,接口还要进行统一拦截,比如接口返回的错误是登录状态过期,那么就需要提示用户跳转到登录页面重新登录。
|
|
|
|
|
|
|
|
|
|
这样,我们就把网络接口中需要统一处理的内容都放在了拦截器中统一处理了。在下面的代码中,所有接口在请求发出之前,都会使用getToken获取token,然后放在header中。在接口返回报错信息的时候,会在调试窗口统一打印报错信息。在项目的组件中,我们只需要直接使用封装好的axios即可。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
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 service
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
然后,我们在项目里集成CSS预编译器,CSS预编译器可以帮我们更快、更高效地管理和编写CSS代码。在这里,我们选择Sass作为CSS预处理语言,然后我们就进入项目根目录下执行下面代码安装Sass。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install -D sass
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
然后,我们进入src/components/Todolist.vue文件中。看下面的代码,我们直接在style标签上新增 lang=“scss”,这样就可以使用Sass的语法了。有了Sass之后,我们在CSS里使用了变量、嵌套、继承等逻辑,并定义了$padding和$white这两个变量。这样我们就可以嵌套书写CSS选择器,也就极大地提高我们写CSS的效率。
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Sass让我们在CSS的世界里也拥有了编程的概念,在实际项目中可以使用变量和函数等概念优化CSS代码,这个你在Element3组件库的实现中也能看到。
|
|
|
|
|
|
|
|
|
|
在Element3的[GitHub项目](https://github.com/hug-sun/element3/tree/master/packages/element3/packages/theme-chalk/src)中,我们可以看到所有的Sass代码。以common/var.scss文件为例,在这个文件中,我们可以看到Element3中所有的变量,并且这个文件中的代码也对颜色、动画函数、边框,字体大小等等都做了统一的设置。
|
|
|
|
|
|
|
|
|
|
我们也可以修改这些变量,从而获得一个定制风格的Element3。所以项目在开始之初,我们就可以想一下整体设计风格,最好能够预先定义好整体的颜色,边框,字体大小等等,这能极大降低后续的css维护成本。
|
|
|
|
|
|
|
|
|
|
至此,一个基于 Vite + Vue 3 + Vue Router + Vuex + Element3 + Axios + Sass 的前端项目开发环境搭建完毕。下面,我们来打磨一下这个项目。**简单来说,就是给项目增加代码规范约束、提交规范约束等,让其更完善、更健壮。**
|
|
|
|
|
|
|
|
|
|
## 代码规范和提交规范
|
|
|
|
|
|
|
|
|
|
由于个人习惯的不同,每个人写代码的风格也略有不同。比如在写JavaScript代码中,有些人习惯在每行代码之后都写分号,有些人习惯不写分号。但是团队产出的项目就需要有一致的风格,这样代码在团队之间阅读起来时,也会更加流畅。ESLint就是专门用来做规范团队代码的一个库。
|
|
|
|
|
|
|
|
|
|
首先我们安装ESLint,进入到项目文件夹,使用下面的命令,我们就可以在全局或者本地安装ESLint了。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm i eslint -D
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
ESLint 安装成功后,在项目根目录下执行 npx eslint --init,然后按照终端操作的提示完成一系列设置来创建配置文件。你可以按照下图所示的选择来始化ESLint。
|
|
|
|
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/45/c0/457e448ee27c431f51ebb525ff1c87c0.png?wh=1258x422)
|
|
|
|
|
|
|
|
|
|
我们设置的是比较松散的校验规则,你可以根据团队风格去额外配置ESLint的插件。我们进入到项目目录下的eslintrc.json中,在rules中新增下面代码,也就是强制要求JavaScript的行尾不写分号。
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
"rules": {
|
|
|
|
|
"semi": ["warn","never"]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
然后,我们在命令行中执行npx eslint src,接着你就会看到下图所示的报错信息,其中详细告诉你了哪里的代码不合规范。根据报错信息的提示,我们进入到router/index.js文件后,删掉第15行代码结束的分号就可以解除这个警告。
|
|
|
|
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/08/c0/087yyf6fce41ededfd59ed39883281c0.png?wh=1480x258)
|
|
|
|
|
|
|
|
|
|
前面我们已经统一了代码规范,并且在提交代码时进行强约束来保证仓库代码的质量。多人协作的项目中,在提交代码这个环节,也存在一种情况:不能保证每个人对提交信息的准确描述,因此会出现提交信息紊乱、风格不一致的情况。
|
|
|
|
|
|
|
|
|
|
对于这种情况,一种比较好的解决方案是,在执行git commit命令的时候,同时执行ESLint。我们使用husky管理git的钩子函数,在每次代码提交至git之前去执行ESLint,只有ESLint的校验通过,commit才能执行成功。后面的进阶开发篇中,单元测试也会放在git的钩子函数中执行,确保提交到git中的代码都是测试通过的。
|
|
|
|
|
|
|
|
|
|
项目代码符合规范后,我们就可以把代码提交到代码仓库中,git允许我们在每次提交时,附带一个提交信息作为说明。我们在项目根目录执行下面的命令,提交了一个附带信息是commit的代码。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
git add .
|
|
|
|
|
git commit -m 'init commit'
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**然后我们需要再定义一下git的提交规范,描述信息精准的git提交日志,会让我们在后期维护和 处理Bug时有据可查**。在项目开发周期内,我们还可以根据规范的提交信息,快速生成开发日志,从而方便我们追踪项目和把控进度。 如下图所示,我们可以看到Vue 3的代码提交日志。
|
|
|
|
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/44/5b/44cfecd1b31deb36b918bd7aa0c9a05b.png?wh=1920x1287)
|
|
|
|
|
|
|
|
|
|
看了Vue 3代码日志提交的格式,初次接触的你可能会觉得复杂。其实不然,Vue 3在代码日志中,使用的是【类别: 信息】的格式,我们可以通过类别清晰地知道这次提交是代码修复,还是功能开发feat。冒号后面的信息是用来解释此次提交的内容,在修复bug时,还会带上issue中的编号。在现在的项目开发中,我们也会强制要求使用和Vue 3一样的git 日志格式。
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
|
|
|
|
今天这一讲的内容到这就结束了,我们来复习一下今天学到的内容。首先我们引入了Element3组件库,在项目入口注册Element3后,你可以在项目的任意地方直接使用[Element3首页的组件列表](https://e3.shengxinjing.cn/#/component/installation)。
|
|
|
|
|
|
|
|
|
|
这样,我们就可以很方便地使用layout和container布局实现页面的搭建,然后引入axios作为网络请求库,并且对接口统一做了全局拦截。下一讲中,项目权限管理也是在axios拦截函数里实现的。
|
|
|
|
|
|
|
|
|
|
当然,复杂的Vue项目更需要良好的规范,毕竟没有规矩不成方圆,为此,我们进一步规范了代码格式,使用ESLint统一JavaScript的代码风格,husky管理git的钩子函数,并且规定了git的提交日志格式,确保代码的可维护性。
|
|
|
|
|
|
|
|
|
|
## 思考题
|
|
|
|
|
|
|
|
|
|
关于Element3组件库布局和导航组件的使用,你有什么其他布局的建议呢?
|
|
|
|
|
|
|
|
|
|
欢迎在留言区发表你的看法,也欢迎你把这一讲的内容推荐给你的同事和朋友们,我们下一讲再见。
|
|
|
|
|
|