Wechat applet monitors this Data change

Posted by xadmin on Fri, 28 Jan 2022 00:46:52 +0100

Problems encountered

In Vue, for data in state, Vue provides a general way to observe and respond to data changes on Vue instances: monitoring attribute watch.

The same situation is encountered in the development of applet: it is necessary to monitor one or more data in the current page data in real time
If there is a general method to monitor, similar problems can be solved uniformly.

Solution

Workers who know Vue two-way data binding must know that its principle is data hijacking. It must be the same if you want to do it in a small program! So here we need to use the object in Javascript Defineproperty () method to manually hijack the getter/setter of the object. For workers who are not familiar with it, please stamp the link 🔗
Object.defineProperty introduction
Back to the applet, if you want to achieve generality, you need to write the monitoring method as a tool class and enter it in the portal app JS, mount to the global, and getApp() in the specific page The method name (parameter) is called, and the call time is in the page life cycle onLoad

Simple implementation (principle)

setWatcher and observe are monitoring logic, watch is a property defined in Page({}), and the configuration function name is the value to be monitored

//index.js
Page({
    data: {
        count: 0
    },
    onLoad(){
        getApp().setWatcher(this.data, this.watch); // Set listening
    },
    // Write a button in the page to trigger this event to change data count
    handleChange(){
	    let val = this.data.count
	    this.setData({ count: ++val })
  	}
  	// Listening events
  	setWatcher(data, watch){
   	 Object.keys(watch).forEach(key => {
   	   this.observe(data, key, watch[key])
   	 })
 	 },
  	observe(obj, key , watchFun){
	   let val = obj[key]
	   	Object.defineProperty(obj, key, {
	     	configurable: true,
	     	enumerable: true,
	    	set: function(value){
	       		watchFun(value, val)
	       		val = value
	  		},
		  	get: function(){
		      return val
		    }
    	})
  	},
  	// The watch property sets the property to be monitored
  	watch:{
    	count:function(newVal, oldVal){
        	console.log('newVal:',newVal);
        	console.log('oldVal:',oldVal);
	    },
	  }
	})

This is not comprehensive enough. What if the monitored value is an object? That requires further improvement of our code

further more

The watch object in Vue can not only write attributes such as name and age, but also write 'my' in quotation marks name’,'my. Attributes such as' age 'listen to the name and age under the my object. We just need to improve the setWatcher method:

	    /**
     * Set listener
     */
    setWatcher(data, watch) {
        Object.keys(watch).forEach(v => {
            let key = v.split('.'); // Attribute in watch with '.' Split into arrays
            let nowData = data; // Assign data to nowData
            for (let i = 0; i < key.length - 1; i++) { // Traverse the elements of the key array, except the last one!
                nowData = nowData[key[i]]; // Point nowData to its key attribute object
            }
            let lastKey = key[key.length-1];
            // Suppose key = ='My Name ', now nowdata = my,lastKey==='name'
            this.observe(nowData, lastKey,watch[v]); // Listen for lastKey of nowData object
        })
    }

At this time, we can listen to a specific attribute of the object. The code can be written like this

	
//index.js
 
Page({
    data: {
        my:{
            name:"xiaoming",
            age:21
        }
    },
    onLoad(){
        getApp().setWatcher(this.data, this.watch);
        this.data.my.name = 'uzi';
        this.data.my.age = 18;
        this.setData({
            my: this.data.my
        })
    },
    watch:{
        'my.name':function(newValue){
            console.log(newValue);
        },
        'my.age':function(newValue){
            console.log(newValue);
        }
    }
})

Depth monitoring

There is a switch for deep listening in Vue. Next, we need to implement deep listening in Vue in the applet, that is, listening to my object can listen to all its internal attributes, attributes of attributes and attributes of attributes... And the this of the listening function should point to this in this page.

	
    /**
     * Set listener
     */
    setWatcher(page) {
        let data = page.data;
        let watch = page.watch;
        Object.keys(watch).forEach(v => {
            let key = v.split('.'); // Attribute in watch with '.' Split into arrays
            let nowData = data; // Assign data to nowData
            for (let i = 0; i < key.length - 1; i++) { // Traverse the elements of the key array, except the last one!
                nowData = nowData[key[i]]; // Point nowData to its key attribute object
            }
            let lastKey = key[key.length - 1];
            // Suppose key = ='My Name ', now nowdata = my,lastKey==='name'
            let watchFun = watch[v].handler || watch[v]; // Compatible with two writing methods with and without handler
            let deep = watch[v].deep; // If deep is not set, it is undefine
            this.observe(nowData, lastKey, watchFun, deep, page); // Listen for lastKey of nowData object
        })
    },
    /**
     * Listen for properties and execute listening functions
     */
    observe(obj, key, watchFun, deep, page) {
        var val = obj[key];
        // Judge whether deep is true and val cannot be empty and typeof Val = = 'object' (deep monitoring is also required for changes in values in the array)
        if (deep && val != null && typeof val === 'object') { 
            Object.keys(val).forEach(childKey=>{ // Traverse every key under the val object
                this.observe(val,childKey,watchFun,deep,page); // Recursive call listener function
            })
        }
        var that = this;
        Object.defineProperty(obj, key, {
            configurable: true,
            enumerable: true,
            set: function(value) {
                // Call with the page object to change the point of this in the function so that this Data access attribute values in data
                watchFun.call(page,value,val); // Value is the new value and val is the old value
                val = value;
                if(deep){ // For deep listening, listen to the object again to listen to its properties.
                    that.observe(obj, key, watchFun, deep, page); 
                }
            },
            get: function() {
                return val;
            }
        })
    }

Topics: Vue Mini Program