Busy as a dog recently, there has been no update for a period of time, although it is a zombie blogger, but a little harvest or to nag about.

As many vUE users know, the principle of vUE bidirectional binding is based on binding properties with getters and setters, triggering view rerendering when properties are changed. This issue also uses these two built-in methods to realize watch in VUE

Getter and setter

A getter is a way to get a property value, and a setter is a way to set a property value.

When the property is assigned a = 1, the setter in the stereotype of A is fired;

Console. log(a) triggers the getter in A’s prototype.

Implement getters and setters

We cannot bind event functions directly to setters and getters of variables. To do this we use Object objects to construct properties with setters and getters.

There are several methods of implementing getters and setters summarized by his predecessor, and he also summarized some implicit properties in Object.prototype that control property enumeration.

I chose an Object. DefineProperty that is easier to construct

The outline object.defineProperty () method directly defines a new property on an Object, or modifies an existing property, and returns the Object. Syntax Object.defineProperty(obj, prop, descriptor) Parameter obj Specifies the Object for which attributes are to be defined. Prop Specifies the name of the property to be defined or modified. Descriptor Descriptor of an attribute to be defined or modified.Copy the code
  1. The first argument, the object to which the constructed property’s this points
  2. The second argument, the name of the property being constructed
  3. The third argument, the rules for construction (described at the end of the text link above)
(functionDefineProperty (o) {var o = {a: 1}// Declare an Object containing a property of 1 object.defineProperty (o,"b",{
        get: function () {
            return this.a;
        },
        set : function (val) {
            this.a = val;
        },
        configurable : true}); console.log(o.b); //==> 1 o.b = 2; console.log(o.b); / / = = > 2}) ();Copy the code

The “B” can be configured freely. The default is false. False words

Object.defineProperty(o,”a”,{set : function(val){}} ); The default value is false.

Construct our Vue.watch

Goal achievement. Here’s what we want to achieve

import watcher from './watcher.js';
let wm = new watcher({
    data:{
        a: 0 
    },
    watch:{
        a(newVal,oldVal){
            console.log('newVal:'+newVal);
            console.log('oldVal:'+oldVal);
        }
    }
})
vm.a = 1 
// newVal:1
// oldVal:0
Copy the code

Create a construct object

class watcher{
    constructor(opts){
        this.$data = opts.data;
        for(let key in opts.data){
            this.setData(key,opts.data[key])
        }
    }

    setData(_key,_val){
        Object.defineProperty(this,_key,{
            get: function () {
                return this.$data[_key];
            },
            set : function (val) {
                const oldVal = this.$data[_key];
                if(oldVal === val)return val;
                this.$data[_key] = val;
                returnval; }}); }}export default watcher;
Copy the code

Add a Watch event trigger

/** * @desc attribute change listener, attribute bysetTo start the watch, Vue watch * @author Jason * @date 2018-04-27 * @constructor * @param {object} opts - constructor parameter @default {data:{},watch:{}}; * @argument {object} data - Property to bind * @argument {object} watch - callback of property to listen on * watch @callback (newVal,oldVal) - New and old values */ class watcher{ constructor(opts){ this.$data = this.getBaseType(opts.data) === 'Object' ? opts.data : {};
        this.$watch = this.getBaseType(opts.watch) === 'Object' ? opts.watch : {};
        for(let key in opts.data){
            this.setData(key)
        }
    }

    getBaseType(target) {
        const typeStr = Object.prototype.toString.apply(target);
    
        return typeStr.slice(8, -1);
    }

    setData(_key){
        Object.defineProperty(this,_key,{
            get: function () {
                return this.$data[_key];
            },
            set : function (val) {
                const oldVal = this.$data[_key];
                if(oldVal === val)return val;
                this.$data[_key] = val;
                this.$watch[_key] && typeof this.$watch[_key] === 'function' && (
                    this.$watch[_key].call(this,val,oldVal)
                );
                returnval; }}); }}export default watcher;
Copy the code
  1. For the sake of robustness within the function,getBaseTypeIt’s used for type verification.
  2. Object.defineProperty(this)This points the context to the current object.
  3. this.$watch[_key].call(this,val,oldVal), bind the context page of the listening event to the current object, so as to obtain the value of the object through this in watch, as follows
let wm = new watcher({
    data:{
        a: 0,
        b: 'hello'}, watch:{ a(newVal,oldVal){ console.log(this.b); }}})Copy the code

conclusion

One might ask why not just use Vue. As you know, VUE is an engineering level framework, so for larger projects, of course vue, React; But is vUE also used to make a display official website or a mobile H5 promotional page? That’s certainly not necessary.

Using the Watcher class, you can keep your page state control organized and traceable.

For example, when several buttons are linked to changes and actions in one or several views, you don’t have to trigger a change for each button click

 btn1.onclick=function(){
    var a = 'haha';
    document.getElementById('id').innerHTML = a;
 }
 btn2.onclick=function(){
    var a = 'xixi';
    document.getElementById('id').innerHTML = a;
 }
Copy the code
let wm = new watcher({
    data:{
        a: "",
    },
    watch:{
        a(newVal,oldVal){
            document.getElementById('id').innerHTML = newVal;
        }
    }
})

btn1.onclick=function(){
    wm.a = 'haha';
 }
 btn2.onclick=function(){
    wm.a = 'xixi';
 }
Copy the code

But if your view is not linked by more than two actions, this may not be useful.