# 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; ``` ![示例](../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, }, ```