zsmj
2 years ago
2 changed files with 219 additions and 0 deletions
After Width: | Height: | Size: 470 KiB |
@ -0,0 +1,219 @@
|
||||
# computed进行列表组件的状态控制 |
||||
|
||||
开发中我们经常使用computed计算属性控制状态,如列表刷新,列表选中等. |
||||
正确理解computed原理并灵活使用 |
||||
|
||||
看如下示例,把选中状态和列表节点放入同一个computed属性中计算,会导致什么问题呢? |
||||
|
||||
```js |
||||
computed: { |
||||
listItems: function () { |
||||
return this.model.items.map(item => { |
||||
return { |
||||
...item, |
||||
type: "bi.text_item", |
||||
cls: "bi-list-item-active2", |
||||
selected: item.value === this.model.selectedValue, |
||||
}; |
||||
}); |
||||
}, |
||||
} |
||||
``` |
||||
|
||||
下面书写了一个简单的demo,可以看到,每次改变selectedValue状态,都会触发列表的重新渲染.当列表节点过多,浏览器性能较差的时候,会出现明显的闪烁或者卡顿. |
||||
|
||||
究其原因,是因为listItems属性的计算同时依赖items和selectedValue属性,每当selectedValue属性改变都会触发listItems的重新计算,返回值是一个新的数据,因此触发watch,重新populate |
||||
|
||||
```js |
||||
let listModel = BI.inherit(Fix.Model, { |
||||
state: function () { |
||||
return { |
||||
selectedValue: 1, |
||||
items: [ |
||||
{ |
||||
text: 1, |
||||
value: 1, |
||||
}, { |
||||
text: 2, |
||||
value: 2, |
||||
}, { |
||||
text: 3, |
||||
value: 3, |
||||
}, { |
||||
text: 4, |
||||
value: 4, |
||||
}, |
||||
], |
||||
}; |
||||
}, |
||||
|
||||
computed: { |
||||
listItems: function () { |
||||
return this.model.items.map(item => { |
||||
return { |
||||
...item, |
||||
type: "bi.text_item", |
||||
cls: "bi-list-item-active2", |
||||
selected: item.value === this.model.selectedValue, |
||||
}; |
||||
}); |
||||
}, |
||||
}, |
||||
|
||||
actions: { |
||||
changeSelectedValue: function (value) { |
||||
this.model.selectedValue = value; |
||||
}, |
||||
|
||||
addItem: function () { |
||||
this.model.items.push({ |
||||
text: this.model.items.length + 1, |
||||
value: this.model.items.length + 1, |
||||
}); |
||||
}, |
||||
}, |
||||
}); |
||||
|
||||
BI.model("list", listModel); |
||||
|
||||
let list = BI.inherit(BI.Widget, { |
||||
|
||||
_store: function () { |
||||
return BI.Models.getModel("list"); |
||||
}, |
||||
|
||||
watch: { |
||||
listItems: function (items) { |
||||
BI.Msg.toast("populate"); |
||||
this.list.populate(items); |
||||
}, |
||||
}, |
||||
|
||||
render: function () { |
||||
return { |
||||
type: "bi.vertical", |
||||
vgap: 15, |
||||
width: 300, |
||||
items: [ |
||||
{ |
||||
type: "bi.button_group", |
||||
ref: ref => this.list = ref, |
||||
layouts: [ |
||||
{ |
||||
type: "bi.vertical", |
||||
}, |
||||
], |
||||
items: this.model.listItems, |
||||
listeners: [ |
||||
{ |
||||
eventName: "EVENT_CHANGE", |
||||
action: (value) => { |
||||
this.store.changeSelectedValue(value); |
||||
}, |
||||
}, |
||||
], |
||||
}, { |
||||
type: "bi.button", |
||||
text: "选中下一个节点", |
||||
handler: () => { |
||||
this.store.changeSelectedValue(this.model.selectedValue + 1); |
||||
}, |
||||
}, { |
||||
type: "bi.button", |
||||
text: "添加一个节点", |
||||
handler: () => { |
||||
this.store.addItem(); |
||||
}, |
||||
}, |
||||
], |
||||
|
||||
}; |
||||
}, |
||||
}); |
||||
|
||||
BI.shortcut("list", list); |
||||
|
||||
|
||||
const ListWidget = BI.createWidget({ |
||||
type: "bi.vertical", |
||||
items: [ |
||||
{ |
||||
type: "list", |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
return ListWidget; |
||||
``` |
||||
|
||||
![示例](https://code.fanruan.com/dailer/FineUI-100-Questions/src/branch/master/images/20.gif) |
||||
|
||||
要如何避免这类问题呢?其实就一句话,将选中状态与列表节点状态分离开.在view层进行控件层面的属性控制 |
||||
|
||||
例如,computed属性中不再控制选中状态,改为单独watch选中状态,调用组件的setValue api 设置选中状态,此时两个状态分离,仅改变选中状态不会触发列表的重新渲染 |
||||
|
||||
```js |
||||
computed: { |
||||
listItems: function () { |
||||
return this.model.items.map(item => { |
||||
return { |
||||
...item, |
||||
type: "bi.text_item", |
||||
cls: "bi-list-item-active2", |
||||
// selected: item.value === this.model.selectedValue, |
||||
}; |
||||
}); |
||||
}, |
||||
} |
||||
|
||||
watch: { |
||||
selectedValue: function (value) { |
||||
this.list.setValue(value); |
||||
}, |
||||
|
||||
listItems: function (items) { |
||||
BI.Msg.toast("populate"); |
||||
this.list.populate(items); |
||||
}, |
||||
}, |
||||
``` |
||||
|
||||
|
||||
为了列表重新渲染之后依旧保持选中值,一般有两种处理方式 |
||||
1. populate之后调用 setValue |
||||
2. 在populate之前进行一次遍历,设置selected属性 |
||||
|
||||
```js |
||||
watch: { |
||||
listItems: function (items) { |
||||
this.list.populate(items); |
||||
this.list.setValue(this.model.selectedValue); |
||||
}, |
||||
}, |
||||
``` |
||||
|
||||
```js |
||||
watch: { |
||||
listItems: function (items) { |
||||
this.list.populate(items.map(item => ({ ...item, selected: item.value === this.model.selectedValue }))); |
||||
}, |
||||
}, |
||||
``` |
||||
|
||||
|
||||
利用响应式新特性,可以更加逻辑清晰的进行选中和列表渲染控制 |
||||
不过响应式并不能解决populate之后状态保持问题,因此需要在populate之前额外设置selected属性 |
||||
|
||||
```js |
||||
{ |
||||
type: "bi.button_group", |
||||
ref: ref => this.list = ref, |
||||
layouts: [ |
||||
{ |
||||
type: "bi.vertical", |
||||
}, |
||||
], |
||||
items: () => this.model.listItems, |
||||
value: () => this.model.selectedValue, |
||||
}, |
||||
``` |
Loading…
Reference in new issue