小程序在page中使用数据监听(不需要setData)

小程序在page中使用数据监听(不需要setData)

众所周知,在小程序中如果想要监听数据,必须得使用组件 Component,它提供了observers 方法用来监听数据变化。但是,其中比较让人不舒服的是,它监听的仅仅是 setData 之后变化的数据。一般来说,在小程序中有些数据是不需要setData 渲染的,比如:input事件,输入框中本身已经展示了数据,是不需要再使用 value 绑定的。

那么问题来了,既然不 setData,那我要是想让另一个数跟着它变怎么办,比如说:我在输入框输入一个数字,它要在上方给我显示它的平方。这个实现起来很简单——将输入的值平方一下再 setData 呗。好吧,这种方法的确可以实现,但是,这样做太弟弟了。(至于为什么太弟弟了,因为不这样说我没办法引出watch数据监听)

大家应该都知道, vue2 中使用 Object.defineProperty 数据劫持来进行双向绑定。那实现 watch 的原理也就呼之欲出了。 Object.defineProperty 中我们用到的属性只需要 setget。不多累述,其余的几个属性可以自行搜索。

const obj = {james:99,melo:97,wade:98,paul:96}
let val
Object.defineProperty(obj,'melo',{
    get(){
        return val
    },
    set(new_){
        console.log('melo的值发生了改变',new_)
        val = new_
    }
})
//---------------------------------

obj.melo = 80

//Output: melo的值发生了改变80

从上面可以看到,当监听的数据发生改变时,set() 就会通知我们,所以,只需要在 set 中添加一个回调函数就可以将改变后的值回调给我们了。下面我们改变 obj 的深度来尝试一下。

const obj = {
    james:{
        Miami:{first:99,second:97},
        Cleveland:{first:98,second:96},
        LosAngeles:{first:96,second:95}
    },
    melo:{
        Denver:{first:95,second:92},
        NYC:{first:96,second:91},
        Portland:{first:82,second:82}
    }
}
let val
Object.defineProperty(obj,'melo',{
    get(){
        return val
    },
    set(new_){
        console.log('melo的值发生了改变',new_)
        val = new_
    }
})

//-------------------------------
obj.melo.Portland.second = 78

//Output: 


小小的脑袋,大大的疑惑。我是哪里写错了吗,怎么没有监听到值的该变呢?至于为什么没有监听到,这个不是一句半句就能说清的,故事远的还要从 helloworld 讲起。看下图:
小程序在page中使用数据监听(不需要setData)_第1张图片

好嘛,原来真的是一句话就能说清,是在下鲁莽了。这句话的意思就是要想实现深层监听,需要将所有 Object 构造一下。也就是说我们需要为所有对象添加 prototype 属性,那么问题就迎刃而解了——遍历每一层,依次 new 一下。开始实现:

const obj = {
    james:{
        Miami:{first:99,second:97},
        Cleveland:{first:98,second:96},
        LosAngeles:{first:96,second:95}
    },
    melo:{
        Denver:{first:95,second:92},
        NYC:{first:96,second:91},
        Portland:{first:82,second:82}
    }
}
class Watch{
    /**
     * 
     * @param {*Object} obj 要监听的集合 
     * @param {*String} key 要监听的字段
     * @param {*Function} fn 发生改变之后通知的函数
     */
    constructor(obj,key,fn){ 
        this.obj = obj
        this.key = key
        this.fn = fn
        this.loop_()
    }
    /**
     * loop_ 是为了深度遍历集合,构造每一个子集合
     */
    loop_(){
        let value = this.obj[this.key]
        if (typeof value == 'object'){
            for(let i of Object.keys(value)){
                new Watch(value,i,this.fn)  //遍历其下所有集合,再进行构造
            }
        }
        this.define_(this.obj,this.key,this.fn) //开始进行数据劫持 
    }

    //数据劫持
    define_(obj,key,fn){
        let val = obj[key]
        Object.defineProperty(obj,key,{
            get(){
                return val
            },
            set(new_){
                console.log('改变',new_,key)
                if(fn)fn(val,new_)  //此时由于每个子集合都挂载了fn,并且都有了prototype属性,
                val = new_          //所以,改变任何一项都会进行通知。
            }
        })
    } 

    
    
}
function print_(val,new_){
    console.log(val+'的新能力值是'+ new_)
}
//-------------------------------
new Watch(obj,'melo',print_)
obj.melo.Portland.second = 78

//Output: melo的新能力值是78


效果不错,问题解决。


这时你可能要说了,vue3 都上线这么多天了,作者不用 Proxy , 还在这炒冷饭,真是个憨憨。那我可就得跟你好好讲一下为什么我不用 Proxy 了。。。

的确,Proxy 比较高性能,而且也可以操作数组,还能使用一些扩展方法。至于不使用的方法我想应该没人反驳,那就是————我,不,会。。。

再见,给本菜鸡点个star吧 github:Jarry007 。

其实,Proxy 在小程序中并不适用,proxy监听不到data数据,它监听的是 new Proxy 生成的新数组,所以不用 Proxy ~~

你可能感兴趣的:(微信小程序,小程序,js)