There are several ways to implement data binding:
- Publisher subscriber mode (backbone.js)
- Dirty value check (angular.js)
- Data hijacking (vue.js)
Dirty value check: angular.js determines whether to update the view by comparing whether the data has changed through dirty value detection. The simplest way is to detect data changes through setInterval() regular polling,
angular enters dirty value detection only when the specified event is triggered, which is roughly as follows:
- DOM events, such as user entering text, clicking a button, etc. ( ng-click )
- XHR response event ($http)
- Browser Location change event ($Location) timer event ($timeout, $interval)
- Execute $digest() or $apply()
Data hijacking: vue.js uses data hijacking combined with publisher subscriber mode to hijack setter s and getter s of various properties through Object.defineProperty(), publish messages to subscribers when data changes, and trigger corresponding listening callbacks.
1. Implement a data listener Observer, which can monitor all the attributes of the data object. If there is any change, it can get the latest value and notify the subscriber
2. Implement an instruction parser Compile, scan and parse the instructions of each element node, replace the data according to the instruction template, and bind the corresponding update function
3. Implement a Watcher, as a bridge between Observer and Compile, which can subscribe to and receive the notification of each attribute change, and execute the corresponding callback function bound by the instruction to update the view
4. mvvm entry function
Implement Observer
ok, the idea has been sorted out, and the relevant logic and module functions have been relatively clear. let's do it. We know that we can use object. Defineproperty() to monitor property changes. Then we will need to recursively traverse the observe d data objects, including the properties of sub property objects Setter and getter In this way, assigning a value to this object will trigger the setter, and then the data change can be monitored.. The relevant code can be as follows:
var data = ; observe(data); data.name = 'dmq'; // Hahaha, the listening value has changed, King -- > DMQ function observe(data) { if (!data || typeof data !== 'object') { return; } // Take out all attribute traversal Object.keys(data).forEach(function(key) { defineReactive(data, key, data[key]); }); }; function defineReactive(data, key, val) { observe(val); // Listening sub attribute Object.defineProperty(data, key, { enumerable: true, // enumerable configurable: false, // Can no longer define get: function() { return val; }, set: function(newVal) { console.log('Hahaha, the monitoring value has changed ', val, ' --> ', newVal); val = newVal; } }); }
In this way, we can already monitor the changes of each data, and then how to notify the subscriber after monitoring the changes. Therefore, next, we need to implement a message subscriber, which is very simple. Maintain an array to collect subscribers. The data changes trigger notify, and then call the subscriber's update method. The code is improved as follows:
// Observer.js // ... omitted Object.defineProperty(data, key, { get: function() { // Since it is necessary to add a watcher in the closure, define a global target attribute through Dep, temporarily store the watcher, and remove it after adding Dep.target && dep.addDep(Dep.target); return val; } // ... omitted }); // Watcher.js Watcher.prototype = { get: function(key) { Dep.target = this; this.value = data[key]; // Here, the getter of the property is triggered to add the subscriber Dep.target = null; } }
So the question is, who is the subscriber? How do I add subscribers to a subscriber? Yes, in the above arrangement of ideas, we have made it clear that the subscriber should be Watcher, and var dep = new Dep(); Yes The defineReactive method is internally defined, so you want to use de
// Observer.js // ... omitted Object.defineProperty(data, key, { get: function() { // Since it is necessary to add a watcher in the closure, define a global target attribute through Dep, temporarily store the watcher, and remove it after adding Dep.target && dep.addDep(Dep.target); return val; } // ... omitted }); // Watcher.js Watcher.prototype = { get: function(key) { Dep.target = this; this.value = data[key]; // Here, the getter of the property is triggered to add the subscriber Dep.target = null; } }
p to add a subscriber, we must operate in the closure, so we can Inside the getter: