You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
6.0 KiB

2 years ago
# 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,
},
```