What is data responsiveness

Data responsiveness is two-way data binding, which is to bind the Model to the view. When we update the model with javascript code, the view is automatically updated. If the user updates the View, the model’s data is automatically updated, which is two-way binding;

Data responsivity principle

The principle of vUE to implement data responsiveness is to use object.defineProperty () to redefine the operation of the Object get attribute (GET) and set attribute (set). However, IN VUe3.0, ES6 proxy object is used to achieve this.

Data responsive implementation

1. First, implement an overall architecture (including MVVM class or VUE class, Watcher class) according to the above figure, using a subscription publisher design pattern;

2. Implement model to View, bind the data in the model to view;

3. Finally, realize view to Model. When view is updated, update corresponding model at the same time;

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Constructor (options) {this.$data = options.data; this.$el = document.querySelector(options.el); // This._deps = {}; this.Observer(this.$data); this.Compile(this.$el); } // Hijack data Observer(data) {for (let key in data) {this._deps[key] = []; var val = data[key]; let watchers = this._deps[key]; Object.defineProperty(this.$data, key, { get: function () { return val; }, set: function(newVal){ if (newVal ! == val) { val = newVal; watchers.forEach(watcher=>{ watcher.update(); }); ----- update view -- subscribe Compile(el) {var nodes = el.children; for (let i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.children.length > 0) { this.Compile(node); } if (node.hasAttribute('v-text')) { var attVal = node.getAttribute('v-text'); this._deps[attVal].push(new Watcher(node, this, attVal, 'innerHTML')); } if (node.hasAttribute('v-model')) { var attVal = node.getAttribute('v-model'); this._deps[attVal].push(new Watcher(node, this, attVal, 'value')); node.addEventListener('input', () => { this.$data[attVal] = node.value; }); }}}} // Subscriber, update view class Watcher {constructor(el, vm, attVal, attr) {this.el = el; this.vm = vm; this.attVal = attVal; this.attr = attr; this.update(); } update() { this.el[this.attr] = this.vm.$data[this.attVal]; }} < / script > < / head > < body > < div id = "app" > < h1 > data responsive < / h1 > < div > <! -- <div v-text="myText"></div> --> <div v-text="myBox"></div> <! -- <input type="text" v-model="myText"> --> <input type="text" v-model="myBox"> </div> </div> <script> const app = new Vue ({el: '# app, data: {/ / myText:' I am a text, myBox: 'I am a box}}); </script> </body> </html>Copy the code

Effect:

proxy

Due to object.defineProperty () following defects:

  1. Cannot detect the addition or deletion of object attributes;
  2. Array length changes cannot be detected (increased length by changing length cannot be detected);
  3. Not because of the limitations of defineProperty, but for performance reasons, not listening on every element of the array;

Vue 3.0 will implement data hijacking using proxy, an enhanced version of Object.defineProperty()

1. Proxy can listen directly on objects rather than properties; 2. Proxy can directly listen to array changes; Object. DefineProperty does not include any of the 13 methods used by Proxy to intercept objects.

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content="ie=edge"> <title> Proxy <body> <div id="app"> <span v-text="myText"></span> <input type="text" v-model="myText"/> </div> <script> class Vue{ constructor(options){ this.el = document.querySelector(options.el); this.data = options.data; this.deps = {}; this.Observer(this.data); this.Compiler(this.el); } Observer(data){ for(let key in data){ this.deps[key] = []; } var deps = this.deps; var proxy = new Proxy(data,{ get:function(target,key,receiver){ return Reflect.get(target,key,receiver); }, set:function(target,key,newVal,receiver){ var oldVal = data[key]; if(oldVal ! = newVal){ let watchers = deps[key]; watchers.forEach(function(watcher){ watcher.update(); }); } return Reflect.set(target,key,newVal,receiver); } }) this.newData = proxy; } Compiler(el){ var childs = el.children; for(let i=0; i<childs.length; i++){ var node = childs[i]; if(node.children.length>0){ this.Compiler(node); } if(node.hasAttribute('v-text')){ var attrVal = node.getAttribute('v-text'); var watcher = new Watcher(node,this,attrVal,'innerHTML') this.deps[attrVal].push(watcher); } if(node.hasAttribute('v-model')){ var attrVal = node.getAttribute('v-model'); var watcher = new Watcher(node,this,attrVal,'value'); this.deps[attrVal].push(watcher); node.addEventListener('input', () => { this.newData[attrVal] = node.value; }); }}}} // Subscriber, update view class Watcher {constructor(el, vm, attVal, attr) {this.el = el; this.vm = vm; this.attVal = attVal; this.attr = attr; this.update(); } update() { this.el[this.attr] = this.vm.data[this.attVal]; ({}} new Vue el: '# app, data: {myText:' this is the content of the input box shows'}}) < / script > < / body > < / HTML >Copy the code