zsmj
3 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