iview级联选择组件源码阅读笔记

组件划分

cascader组件共划分成了三个组件:

  • cascader.vue
  • caspanel.vue
  • casitem.vue

其中,cascader是最外层的组件,包括一个slot和caspanel。slot内部默认是一个input框。

caspanel则是弹出的面板,里面包括若干个casitem选项,和一个子caspanel面板(如果有)。

casitem则是简单的选项组件,存放选项。

DOM结构

大致的DOM结构如下:

1
2
3
4
5
<Cascader>
<Input>
<Caspanel>
<Caspanel>
<Casitem>

逻辑思路

在页面渲染时,通过computed中的querySelections方法对data进行遍历修改,添加__label __value属性

在点击slot中的内容时,调用toggleOpen方法,修改visible属性控制Caspanel的显隐:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//以下三个函数用于控制this.visible,由此控制panel的显隐
handleClose () {
this.visible = false;
},
toggleOpen () {
if (this.disabled) return false;
if (this.visible) {
if (!this.filterable) this.handleClose();
} else {
this.onFocus();
}
},
onFocus () {
this.visible = true;
if (!this.currentValue.length) {
this.broadcast('Caspanel', 'on-clear');
}
},

然后在watch中监听visible属性的变化,当input中有值时,更新面板的内容,使面板展示选中的项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
visible (val) {
if (val) {
if (this.currentValue.length) {
this.updateSelected();
}
if (this.transfer) {
this.$refs.drop.update();
}
} else {
if (this.filterable) {
this.query = '';
this.$refs.input.currentValue = '';
}
if (this.transfer) {
this.$refs.drop.destroy();
}
}
this.$emit('on-visible-change', val);
},
1
2
3
4
5
6
7
updateSelected (init = false) {
if (!this.changeOnSelect || init) {
this.broadcast('Caspanel', 'on-find-selected', {
value: this.currentValue
});
}
},

updateSelected中,会广播on-find-selected事件,在处理该事件的函数中,还会触发on-clear事件,将Caspanel的子面板都清空,然后再渲染新的子面板(展示选中的项),并设置选中的value。

当用户点击最后要选择的选项时,Caspanel组件会调用一个方法先改变父组件Cascaderthis.tmpSelected,然后触发on-result-change事件,驱使父组件更新this.selectedthis.currentValue,从而更新DOM。

疑惑:

Caspanel组件中,使用了this.$parent来调用父组件Cascader的方法,甚至用到了this.$parent.$parent,在其他组件中有看到直接通过this.$parent修改父组件中的属性值,不知这种方式与通过事件来通知父组件从而改变父组件中的属性,哪种更合适一些。

1
2
3
4
5
6
7
emitUpdate (result) {
if (this.$parent.$options.name === 'Caspanel') {
this.$parent.updateResult(result);
} else {
this.$parent.$parent.updateResult(result);
}
},