一种原来关于ko数组使用方式的错误理解的修正

@songhlc 2017-10-09 02:53:15发表于 yonyouyc/blog knockout原创

原来对于knockout没有理解渗透,一直有错误的使用方式。

场景:ko里监听一个数组如 arrays: ko.observableArray([{name:1,id:2},{name:2,id:3}])

我们知道如果我们直接这么处理

arrays()[0].id = 3

虽然数据改变了但是并不会触发ko的监听事件所以导致页面不会刷新

之前错误的处理方式(或者说效率不高的处理方式):

var data = arrays()
arrays([])
arrays(data)

先清空后触发由于数组长度改变了 所以就会触发ko的界面更新。

前段时间看了ko的官方文档发现对于数组对象的监听ko支持这几种方式

pop, push, shift, unshift, reverse, sort, splice

注意到这里有splice方法。

splice方法用法最常用的是:

var array = [1,2,3]
//表示的是从索引0开始删除1条记录,并返回那一条记录
array.splice(0,1)

但其实splice还有另外一个用法

array.splice(0,1,4) 
//这里三个参数表示从索引0 开始,删除1条记录,并在原来索引0的位置插入一条值为4的记录

同样vue的官方文档上也建议当数组内部属性改变时使用

Vue.set(example1.items, indexOfItem, newValue)

example1.items.splice(indexOfItem, 1, newValue)

两种方式来使vue检测到对象数组改变

所以一开始的写法改良一下

var item = arrays()[1]
item.id = 3
arrays.splice(1,1,item)

即可完成让ko能监听到对象数组的改变了(相对肯定比原来的方式更高效)

然后在写demo的时候发现这种方式在ko中完全不可用

深入分析问题所在:回到之前提到过的引用赋值,可以发现item和arrays()[0]引用的是同一个内存地址,
而只有在执行splice方法的时候ko才能触发检测变动,所以当运行到了arrays.splice的时候要替换的item和原来ko数组里对一个的第一项数据其实是一样的,所以在ko检测的时候就发现值没有改变。。。

于是乎只好这么来处理

var item = JSON.parse(ko.toJSON(arrays()[1]))
item.id = 3
arrays.splice(1,1,item)

对于普通json对象最简单的深复制的方法,也可以去学习一下 深入剖析 JavaScript 的深复制

回过头来 为什么vue里没有这个问题这个我们下期再谈,毕竟二者的实现方式是不一样的