概述
本篇笔者来讲解一下 tree-select
的实现原理和细节处理,结合实际场景会对其进行拓展,以及优化用户体验问题,以便满足更多的业务场景。当然笔者会结合自身的理解,已经为每个核心的方法增加了必要的注释,会尽最大努力将其中的原理讲清楚,若有不妥之处,还望不吝赐教,欢迎批评指正。
预览
优化前 |
优化后 |
---|---|
原理
tree-select
的层级主要由 侧边导航栏 和 内容页(Content)
组成,层级结构也非常清晰明了,业务逻辑也比较简单,主要用到技术可能就是双向绑定
,比如:main-active-index
表示左侧高亮选项的索引,active-id
表示右侧高亮选项的 id。
emit(ctx, 'update:main-active-index', index);
emit(ctx, 'update:active-id', newActiveId);
拓展
tree-select
在我们项目中使用的还算高频,尽管组件易用,原理简单的同时,也存在些许美中不足,其主要还是用户体验的问题,本次tree-select
拓展就是优化用户体验。项目中用户反馈最多的两个问题如下:
- 左侧导航栏和右侧内容能滚动时,点击左侧选项和点击右侧选项时,能滚动到
tree-select
的中间位置。 - 由于移动端大多数都是
keep-alive
模式,当tree-select
可视时,左侧高亮选项和右侧选项也得可视。
上面看似两个问题,实则是一个问题,只要我们在用户点击左侧选项和点击右侧选项,以及在activated
方法中,将此选项滚动到中间位置即可。具体实现如下,大家看看代码就会一目了然。
export default createComponent({
props: {
max: {
type: [Number, String],
default: Infinity,
},
items: {
type: Array,
default: () => [],
},
height: {
type: [Number, String],
default: 300,
},
activeId: {
type: [Number, String, Array],
default: 0,
},
selectedIcon: {
type: String,
default: 'success',
},
mainActiveIndex: {
type: [Number, String],
default: 0,
},
},
data() {
return {
}
},
computed: {
isMultiple(){
return Array.isArray(this.activeId);
}
},
watch: {
// 也需要监听左侧导航栏索引的变化,但是滚动时不加动画,因为此值有可能是异步设置的
mainActiveIndex(val) {
// 这种场景直接滚动
this.scrollIntoNavView(+val, true)
},
// 也需要监听右侧内容栏索引的的变化,但是滚动时不加动画,因为此值有可能是异步设置的
activeId() {
// 这种场景直接滚动
this.scrollIntoContentView(this.getCurrentContentIndex(+this.mainActiveIndex), true)
},
// 数据源变化了 设置一下
items(){
this.init()
}
},
async mounted() {
await this.$nextTick()
// 获取滚动器
const {
navScroller, scroller } = this.$refs
this.navScroller = navScroller.$el
this.scroller = scroller
// 滚动指定的nav和content索引
this.scrollToView(true)
},
activated() {
this.scrollToView(true)
},
methods: {
async init() {
// 这里要置空
this.lastScrollActiveId = null
await this.$nextTick()
// 滚动指定的nav和content索引
this.scrollToView(true)
},
isActiveItem(id) {
return this.isMultiple
? this.activeId.indexOf(id) !== -1
: this.activeId === id;
},
renderContent() {