Let's supply this picture first
0. What is responsive
Explanation on the official website (you can read it selectively)
When you pass an ordinary JavaScript object into the Vue instance as the data option, Vue will traverse all the properties of the object and use object Defineproperty converts all these properties into getters / setters. Object.defineProperty is a feature in ES5 that cannot shim, which is why Vue does not support IE8 and earlier browsers.
These getters / setters are invisible to the user, but internally they enable Vue to track dependencies and notify changes when properties are accessed and modified. It should be noted that different browsers format getters / setters differently when printing data objects on the console, so it is recommended to install Vue devtools to obtain a more user-friendly interface for inspection data.
Each component instance corresponds to a watcher instance, which will record the "contacted" data property as a dependency during component rendering. Then, when the setter of the dependency is triggered, it will notify the watcher to re render its associated components.
Note:
- shim can introduce new API s into the old environment, and only rely on the existing means in the environment.
1. The key to achieving responsiveness
- Object.defineProperty
- Subscriber design pattern
2. Object.defineProperty() method
Object. The defineproperty () method defines a new property directly on an object, or modifies an existing property of an object and returns the object.
grammar
Object.defineProperty(obj, prop, descriptor)
parameter
- obj
The object whose properties are to be defined. - prop
The name or Symbol of the attribute to define or modify. - descriptor
The property descriptor to define or modify. (object)
Return value
The object passed to the function.
The key lies in the set and get methods in the passed descriptor parameter
Let's combine chestnuts
First, we define an object to simulate data in vue
var obj = { message: 'ha-ha', name: 'perry' };
Then loop through the keys inside and call the defineProperty method for each property in obj
Object.keys(obj).forEach(key => { let value = obj[key] Object.defineProperty(obj, key, { set(newValue) { // Call the set method when changing the attributes in obj (key) console.log("set"); value = newValue }, get() {// Call the get method when getting the properties in obj (key) console.log("get"); return value } }) })
Do you have an idea? Yes, we can do a lot in the set and get functions
3. Publish subscribe mode
Publish subscribe mode defines a one to many relationship between objects, allowing multiple observer objects to listen to a topic object at the same time. When the topic object changes, all objects that depend on it will be notified. (understand this sentence well!!!)
See an example to help understand
You pay attention to a on the microblog, and many other people pay attention to a, so when a releases the news, the microblog will push the news for you. A is the publisher, you are the subscriber, and microblog is the dispatching center. You and a do not have direct information exchange, but are coordinated through microblog
(your attention (subscription), A's publishing dynamics (Publishing)).
Take a direct look at its simple model:
// Publisher class Dep { constructor() { this.Subscription = [] // Used to record all subscribers } addSub(watcher) { // Join subscriber this.Subscription.push(watcher) } notify() { // Notify each subscriber of updates this.Subscription.forEach(item => { item.update(); }) } } // Subscribers, also known as observers class Watcher { constructor(name) { this.name = name; } update() { console.log(this.name + "happen update"); } }
4. Combination of the two
- We call the publisher's notify in set, which is to notify all subscribers to update as long as the obj changes.
// 1. Traverse all keys in obj Object.keys(obj).forEach(key => { let value = obj[key] // 2. Listen for changes through the defineProperty method Object.defineProperty(obj, key, { set(newValue) { // When changing the properties in obj, here value = newValue dep.notify(); // Then, the subscriber of the corresponding dep (need to make judgment) is notified in this place. Different DEPs need to send notifications for different value changes (key) console.log('monitor' + key + 'change' + ':' + value) }, get() { // This is where the properties in obj are obtained console.log('obtain' + key + 'Corresponding value') return value } }) })
- Then we create a publisher object and add subscribers to it
A dep object in an instance can be understood as what we need to do when adding data in data
Instantiating a watcher object and calling dep.addSub is what we need to do when using data
// Instance is a dep object that simulates a data added to a data const name = new Dep(); const w1 = new Watcher('Zhang San'); // Zhang San used it name.addSub(w1) // Zhang San becomes a subscriber and is placed in the subscription array const w2 = new Watcher('Li Si'); //Li Si used it name.addSub(w2) // Li Si becomes a subscriber and is placed in the subscription array
- Then, if we change the obj attribute, we will notify all subscribers
Are you clear?
Not clear. Let's take a look at the complete code. There are only two classes and one key logic
var obj = { message: 'ha-ha', name: 'why' }; Object.keys(obj).forEach(key => { let value = obj[key] Object.defineProperty(obj, key, { set(newValue) { value = newValue name.notify(); // Then notify the corresponding dep subscriber of the change in this place }, get() { return value } }) }) // Publisher class Dep { constructor() { this.Subscription = [] // Used to record all subscribers } addSub(watcher) { // Join subscriber this.Subscription.push(watcher) } notify() { // Notify each subscriber of updates this.Subscription.forEach(item => { item.update(); }) } } // Subscribers, also known as observers class Watcher { constructor(name) { this.name = name; } update() { console.log(this.name + "happen update"); } } // Instance a dep object const name= new Dep(); const w1 = new Watcher('Zhang San'); name.addSub(w1) // Zhang San becomes a subscriber and is placed in the subscription array const w2 = new Watcher('Li Si'); name.addSub(w2) // Li Si becomes a subscriber and is placed in the subscription array
reference resources
- MDN
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty - vue official documents
https://cn.vuejs.org/v2/guide/reactivity.html - codeWhy teacher's notes