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
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,
|
||
|
},
|
||
|
```
|