最近开始写表单列表展示数据的业务项目了,踩了一些这类型项目的坑,总结如下
不过因为总结的不止一个项目,所以举例用到的UI库有手机端的Vant和PC端的ElementUI等。
1. 用户体验性:
1)loading
(1)通用loading
<!-- 先隐藏,等待内部页面的接口加载完成后,再展示页面 -->
<div
v-show="!isLoading"
]"
>
<router-view
@changeLoading="isLoading = false"
></router-view>
</div>
<!-- 通用loading页 -->
<div v-if="isLoading" class="loading-page">
<!-- 此代码的项目是手机端所以用了vant组件,可换 -->
<van-loading color="#329bff" vertical><p>加载中...</p></van-loading>
</div>
(2)单独loading
组件 | Element-Loading 加载-区域加载
项目中若有特定改动,可参照源码。
2)操作后提示
让用户知道自己每个操作都是明确且有效的。
有问题,提示下一步解决操作。
没问题,提示操作成功 或 即刻有明显的界面反馈。
(1)表单错误提示
表单验证表单通用防错设置:
- 每个输入框都必须设置最大长度,具体长度可以和后端商量,防止数据传给后端出现内存溢出、数据库写入长度限制等问题。
- 必要的表单项如必填项必须设置验证规则,以触发错误提示。
- 提交表单数据的时候必须对整个表单进行校验validate,让你的验证规则生效。
- 如果表单在该页面重复使用(如在对话框中,每次使用时重复弹出同一节点的该对话框),必须在使用完后或将使用前对表单整体作一次数据重置和校验重置
(2)请求反馈提示
因为请求的响应依赖于网络传输,无法确定是否会快速响应。如果出现了页面上的停顿无反应,会给用户造成——我是否刚刚操作有问题怎么没有出现我刚刚操作的数据反馈,这样的感觉(not good)。
此时就可以用到几种加载中提示:
- 上述第一条讲到的大面积loading,来给用户在未收到响应前作缓冲。
如果是增删查,一般可以把loading放在整个列表元素上。
如果是单个项的修改,则可以小面积地放在单个项元素上。
注:请求操作一般就是增删改查数据了,除了指定不展示loading的情况(如滚动加载loading就不覆盖列表了,展示在底部即可),基本都会有个缓冲。
-
也可以用小的loading图标,在预测用户会第一眼看到的地方提示,比如:
对话框点击确定后,可以把确定按钮做成loading(等等,自由发挥),此时记得把按钮也做成不可点击发送请求的状态。
按钮loading
列表滚动加载时,底部提示。
列表滚动加载
3)防止误操作——确认框
组件 | Element-MessageBox 弹框-确认消息
删除确认框常用于一些不可恢复的简易操作,如通过简单的单击就能触发的删除,点击后先弹框确认,以防是用户误点。
4)空状态
Vant - Empty 空状态
当数据加载为空时,需要给用户展示空状态占位图,否则空白一片,也是没有反馈的一种(这种查询请求反馈一般不弹消息框)。
一般数据加载空状态即可,也可根据具体业务细分:数据加载空状态、图片空状态、搜索后空状态等。
5)数据重置
同一个组件在同一个页面上多次使用,每次重新使用,要把组件内的初始数据重置。
比如:对话框 组件 | Element-Dialog 对话框-自定义内容
如图,我填完表单再次打开对话框,并没有如期望的数据重置,以方便我省去删除操作填写下一次表单。
因此,需要数据重置。
6)数据更新
一个页面会由不同组件组成,有些组件之间有数据关联。
当没有把有关联的数据提取到父组件中去时,这时需要注意在一处数据改变后,应把另一处数据也同时更新掉。
当把有关联的数据提取到父组件中去时,注意改动的必须是父组件的数据,才会层层更新至各个子组件。
这种多用处的数据,最好提取至父组件中。
2. 注意性能
1)非特殊情况,同一接口在一个页面中尽量不要在上一次请求未响应前就开始下一次请求。
比如滚动加载,此时的加载接口响应会跟不上用户的操作,此时就需要控制,一次只作一个请求。
2)节流和防抖
(1)节流:控制一段时间内只执行一次
对于一些偶尔会因为用户误操作产生的持续性触发,如按钮点击,可以采用节流。
如果设置了上述第二条讲的按钮loading反馈情况,其实基本就不会碰到说一个请求在未反馈时重复发多次。
但是,也可以节省一下流量。
比如,搜索按钮,一般来说,用户输入不同的条件,都需要时间,基本都会超过500ms,或者保险点300ms(具体可自己尝试),为了杜绝双击的情况,可以让这个按钮在一段时间内只能点击一次,即节流
(2)防抖:无操作的一段时间后触发一次
在一些经常会持续性触发的事件监听中,如滚动,最好采用防抖或节流来控制一段代码高频率运行。
比如我在滚动中需要作图片懒加载,此时就要采用防抖策略,当我滚动停止时,再加载图片,就能既不影响滚动,又能让用户看到想看的图片。
总结:小心持续性触发的操作。
防抖和节流简单版本如下:
data() {
return {
timer: null, // 可以不放在data中,来节省内存,因为vue自动加上数据劫持
}
},
beforeDestroy() {
if (this.timer) { // 虽然对于timeout定时器只要不嵌套总有结束的时候,但是这是保险嘛,以防影响其他页面
clearTimeout(this.timer)
this.timer = null
}
},
methods: {
/**
* @description: 防抖,目前没有碰到要立即执行的情况,所以暂时不写立即执行版本
*/
debounce() {
if (this.timer) { // 若存在,则消掉这个定时器
clearTimeout(this.timer)
this.timer = null
}
this.timer = setTimeout(() => { // 开始新一波定时
this.execute() // 操作
}, 500)
},
/**
* @description: 节流,非立即执行
*/
throttleImmediate() {
if (!this.timer) {
this.timer = setTimeout(() => {
this.execute() // 操作
this.timer=null
}, 500)
}
},
/**
* @description: 节流,立即执行
*/
throttleNonImmediate() {
if (!this.timer) {
this.timer = 1 // 提前置非空,以防执行操作期间,再次触发
this.execute() // 操作
this.timer = setTimeout(() => {
this.timer=null
}, 500)
}
},
...
以上定时器也可以用时间戳代替,更加精确。
3)节省请求
一般我们在增删改数据之后,会需要重新查询数据,这是比较保守的方法,一般不会出错。
但是考虑到每个操作之后都要重新请求一遍数据浪费带宽。
所以自行权衡一下,如果该数据一般不会有多人同时查看和改动:
这种情况下,如果一个用户作了修改,并不会影响到其他用户,所以可以直接在增删改请求成功后,前端根据用户的填入数据自行更改页面的显示数据,而无需再请求一次查的接口。
3. 通用提取
1)通用头部、底部
/* App.vue */
<div
v-show="!isLoading"
:class="[
'page-content',
{
'show-top': $route.meta.showTop,
'show-bottom': $route.meta.showBottom
}
]"
>
<router-view
ref="RouterView"
:key="$route.fullPath"
@changeManage="changeManage"
@changeLoading="isLoading = false"
></router-view>
</div>
<header v-if="$route.meta.showTop">...</header>
<footer v-if="$route.meta.showBottom">...</footer>
(1)可给router-view提供一些方法,方便子组件修改外部通用样式,如@changeManage="changeManage"
就是提供方法给子组件修改头部右侧的按钮文字。
(2)可使用key来对每个router-view标识:key="$route.fullPath"
,以免/page/1 ⇒ /page/2 或者 /page?id=1 ⇒ /page?id=2 这类链接跳转时不执行created, mounted之类的钩子(Vue会复用相同组件);或添加beforeRouteUpdate钩子来执行相关方法拉取数据。
不同key值的/page/1 ⇒ /page/2,钩子加载顺序beforeRouteUpdate => created => mounted
相同key值的/page/1 ⇒ /page/2,钩子加载顺序beforeRouteUpdate
(3)可以通过router中的meta数据来控制页面结构和样式,如下
...
/* router.js */
{
name: "Home",
path: "home",
meta: {
title: "首页",
index: 1,
showTop: false,
showBottom: true
},
component: () => import("./views/Personal/Home.vue")
},
...
2)通用组件提取
Vue本身提供了组件的复用选项,一个是最常用的components,可以把组件内部的通用模块提取为子组件;mixins,常用于把script部分的通用的选项提取出来,需要时再混入使用;extends,比components更霸道的组件复用,直接把整个被继承的组件整合进本组件,template、script、style都可以整合。而我们自始流传的抽取通用方法common.js也是一个把通用方法和属性(如,表单常见的手机号正则和校验方法)抽取出来复用的经典办法。
(1)components
常用于 几个页面组件内部有类似的模块,把这种类似的模块抽取出来。
不同的部分,以props传入区分改变。
(2)mixins
常用于 几个页面组件的vue选项相同,把相同的抽取出来。
不同的部分,以在当前组件写同名的选项覆盖。
注:此处除了data和methods会覆盖展示外,其余的都是合并展示,所以如果是合并展示的选项也想作通用的话,可以把不同的部分通过调用方法,来进行不同的操作。
比如:
// mixins.js
watch: { // watch是合并展示的
currentIndex(val) {
// 相同的代码
this.doDifferent()
}
},
methods: {
doDifferent() {
// mixins里面可以不写,具体内容写到需要混入的组件中去
}
}
// test.vue
import testMixin from '@/mixins/mixins.js'
export default {
...
mixins: [testMixin],
methods: {
doDifferent() { // 这样就可以通过方法的覆盖,达到让watch进行不同的操作的效果。
// 在该组件的情况下的对于currentIndex改变的操作代码
}
(3)extends
extends和mixins不同的是相当于是复制了一整个组件过来。
(4)common.js
此处可以存放常量、通用方法等等,类似于java的写法。如果代码过多,可以根据模块分文件整合。
网友评论