preface

  • I was studying the other dayvueIn vUE’s data bidirectional binding, we learned about the “publish – subscribe design pattern”, which was previously only superficial in vUE’s data bidirectional binding, knowing that bidirectional binding relies on native JSObject.defineProperty()In thesetget, and the specific implementation is not very well understood. Recently, I also learned that the concept is realized by the design idea of “publish – subscribe design pattern”, so the following simple implementation is to consolidate the knowledge of this aspect and deepen my understanding.

Implement a listener

  • As the name implies, listeners are primarily used to listen for changes in data or for fetching data, and this is an obvious exampleObject.defineProperty()In thesetget, the rough code is as follows:
const foo = {}; Object.defineproperty (foo, "value", {get() {console.log(" data was heard here "); }, set(newVal) {console.log(" I heard a new value set here "); }});Copy the code

However, there are many properties in the object returned by vue’s data method, so we need to loop through the properties of each object that we need to listen to, so at this time we need a function method to listen to the property of the object ————. This is the listener we want to implement, that is, to listen to the value of each property on the object, So change the above code to look like this:

function Observer(obj) { Object.keys(obj).forEach((key) => { Object.defineProperty(obj, key, { configurable: True, Enumerable: true, writable: true, get() {console.log(" this is listening to get data "); }, set(newVal) {console.log(" I heard a new value set here "); }}); }); }Copy the code

And then you call the listener

const data = Observer({ name: "zhangsan", age: 18 });
Copy the code

So we’ve implemented the listener.

Implementing a Subscriber (Dep)

  • Where there are subscribers, there are subscribers, and the purpose of a subscriber is to collect subscribers, because there can be many subscribers and there can be only one subscriber, so the purpose of a subscriber is to manage all of them. These are things that the subscriber subscribing to, and the subscriber side can receive a listener from the response to notify the subscriber that there has been a change.

So the key is the last sentence, which is shown below:

So the subscriber has two functions: one is to collect subscribers (watcher), and the other is to perform notification changes, which is code like this:

function Dep() { this.subs = []; } Dep.prototype = { addSub(sub) { this.subs.push(sub); }, notify() {this.subs.foreach ((sub) => {sub.update(); // Perform the subscriber update operation}); }}; Dep.target = null;Copy the code

Explain this string of code:

  • First of all, it statesDepThe function object
  • We then declare two methods on the function object:addSubnotify, respectively, to collect subscribers and perform notifications of changes
  • The lastDep.target = nullDeclaration in global, used to store the current subscriber

Then, once you have the subscriber, you need to override the listener, both to add the subscriber to the subscriber and to notify the subscriber when the value changes again

function Observer(obj) { const dep = new Dep(); Object.keys(obj).forEach((key) => { Object.defineProperty(obj, key, { configurable: true, enumerable: true, writable: True, get() {console.log(" this is listening to fetch data "); if(Dep.target ! = null) {de.addsub (de.target); }}, set(newVal) {console.log(" here I hear a new value set "); dep.notify() }, }); }); }Copy the code

Now that the subscriber is created, the next step is how to take advantage of the subscriber by passing our subscribers to it. So the next step is to create subscribers.

Implement a subscriber (Watcher)

  • As described by the subscriber, there are two things we need to care about the subscriber. One is that the subscriber needs to perform an update operation when notified of a change message. The second is to require subscribers to accept the data they know, the code is as follows:
Function Watcher() {} watcher.prototype = {update() {}, // get() {}, // get() {};Copy the code

Next, focus on how to update and how to get the current value.

  • First of all,WatcherThe function object declares the update instance, the updated node property, a callback method to perform the update, and the current value, soWatcherThe function will have something like this:
function Watcher(vm, exp, cb) { this.vm = vm; this.exp = exp; this.cb = cb; this.value = this.get(); // Get the current value}Copy the code
  • And then we seethis.value = this.get()Here the prototype itself is calledgetter, so, next, refine this method:
get() { Dep.target = this; Const val = this.vm. Data [this.exp]; // const val = this.vm. Dep. Target = null; Dep. Target = null; // Free the global variable return val; }Copy the code
  • The last one that needs to be updatedupdate()Method, which replaces the old value with the new value
update() {
    const newVal = this.vm.data[this.exp];
    const oldVal = this.value;
    if (newVal != oldVal) {
      this.cb(newVal);
    }
  }
Copy the code

So we have subscribers

conclusion

In this way, we simply realize the “publish – subscribe” design pattern, also a good understanding of vUE data two-way binding principle, the author is also the front end of the small white, is still trying to study vUE source code. Through each small step of learning, I gradually understand the underlying and thought of VUE. Also slowly deepened their understanding of native JS.

And this is just a part of the “publish – subscribe” design pattern, the specific complete code and vUE data two-way binding source code analysis please refer to the article, the author is also through this article with a poor understanding and write, and learn from the big guys.

The literature

[0 to 1 master: Vue core bidirectional data binding]juejin.cn/post/684490…