微信小程序通过什么方法可以检测数据变化

2018-05-15 17:13

摘要:小程序中函数setData()可以检测数据。此外,小程序还可以watch和computed检测数据

vue中,computed是一个计算属性,类似于过滤器,对绑定到view的数据进行处理,并监听变化。而watch监听复杂数据类型需用深度监听。这两者都可以在vue上实现检测数据的变化。而微信小程序不同于vue可以使用watchcomputed做出相应的改变。小程序中只有函数this.setData()可以检测数据,所以小程序每次数据改变需要检测时都必须手动执行函数才可实现。除此之外,小程序还可以附上这两个功能检测数据变化。

微信小程序通过什么方法可以检测数据变化

vue 里是通过 Object.defineProperty 来实现数据变化检测的,给该变量的 setter 里注入所有的绑定操作,就可以在该变量变化时带动其它数据的变化。实际上,在小程序里实现要比 vue 里简单,应为对于 data 里对象来说,vue 要递归的绑定对象里的每一个变量,使之响应式化。但是在微信小程序里,不管是对于对象还是基本类型,只能通过 this.setData() 来改变,这样我们只需检测 data 里面的 key 值的变化,而不用检测 key 值里面的 key

测试代码:

Page({

data: {

test: { a: 123 },

test1: \'test1\',

},

onLoad() {

computed(this, {

test2: function() {

returnthis.data.test.a + \'2222222\'

},

test3: function() {

returnthis.data.test.a + \'3333333\'

}

})

watch(this, {

test:function(newVal) {

console.log(\'invoke watch\')

this.setData({test1: newVal.a + \'11111111\' })

}

})

},

changeTest() {

this.setData({ test:{ a: Math.random().toFixed(5) } })

},

})

现在我们要实现 watch computed 方法,使得 test 变化时,test1test2test3 也变化,为此,增加了一个按钮,当点击这个按钮时,test 会改变。

watch 方法相对简单点,首先我们定义一个函数来检测变化:

function defineReactive(data, key, val, fn) {

Object.defineProperty(data, key, {

configurable: true,

enumerable: true,

get: function() {

return val

},

set: function(newVal){

if (newVal === val)return

fn &&fn(newVal)

val = newVal

},

})

}

然后遍历 watch 函数传入的对象,给每个键调用该方法

function watch(ctx, obj) {

Object.keys(obj).forEach(key => {

defineReactive(ctx.data, key, ctx.data[key], function(value) {

obj[key].call(ctx,value)

})

})

}

这里有参数是 fn ,即上面 watch 方法里 test 的值,这里把该方法包一层,绑定 context

接着来看 computed,这个稍微复杂,因为我们无法得知 computed 里依赖的是 data 里面的哪个变量,因此只能遍历 data 里的每一个变量。

function computed(ctx, obj) {

let keys =Object.keys(obj)

let dataKeys =Object.keys(ctx.data)

dataKeys.forEach(dataKey => {

defineReactive(ctx.data, dataKey, ctx.data[dataKey])

})

let firstComputedObj =keys.reduce((prev, next) => {

ctx.data.$target =function() {

ctx.setData({[next]: obj[next].call(ctx) })

}

prev[next] =obj[next].call(ctx)

ctx.data.$target =null return prev

}, {})

ctx.setData(firstComputedObj)

}

详细解释下这段代码,首先给 data 里的每个属性调用 defineReactive 方法。接着计算 computed 里面每个属性第一次的值,也就是上例中的 test2test3

computed(this, {

test2: function() {

returnthis.data.test.a + \'2222222\'

},

test3: function() {

returnthis.data.test.a + \'3333333\'

}

})

这里分别调用 test2 test3 的值,将返回值与对应的 key 值组合成一个对象,然后再调用 setData() ,这样就会第一次计算这两个值,这里使用了 reduce 方法。test2 test3 都是依赖 test 的,这样必须在 test 改变的时候在其的 setter 函数中调用 test2 test3 中对应的函数,并通过 setData 来设置这两个变量。

声明了一个变量来保存所有在变化时需要执行的函数,在 set 时执行每一个函数,因为此时 this.data.test 的值还未改变,使用 setTimeout 在下一轮再执行。现在就有一个问题,怎么将函数添加到 subs 中。不知道各位还是否记得上面我们说到的在 reduce 里的那两行代码。因为在执行计算 test1 test2 第一次 computed 值的时候,会调用 test getter 方法,此刻就是一个好机会将函数注入到 subs 中,在 data 上声明一个 $target 变量,并将需要执行的函数赋值给该变量,这样在 getter 中就可以判断 data 上有无 target 值,从而就可以 push subs,要注意的是需要马上将 target 设为 null

到此为止已经实现了 watch computed,但是还没完,有个问题。当同时使用这两者的时候,watch 里的对象的键也同时存在于 data 中,这样就会重复在该变量上调用 Object.defineProperty ,后面会覆盖前面。因为这里不像 vue 里可以决定两者的调用顺序,因此我们推荐先写 computed 再写 watch,这样可以 watch computed 里的值。


推荐阅读:微信小程序如何实现接入富文本编辑    微信小程序开发教程 

参与讨论

发表评论

扫码添加专属客服即可随时咨询

还可领取小程序推广攻略

咨询热线

13312967497

扫码添加业务即可随时咨询 还可领取小程序推广攻略

业务咨询: 13312967497
扫码咨询

扫码咨询套餐

回到顶部