preface

Vue’s data response relies mainly on Object.defineProperty(), so how does the whole process work? To follow the path of Vue in our own way, which is actually to end with the principles of Vue, let’s reverse the implementation.

Typeof obj === ‘object’ if(typeof obj === ‘object’); if(typeof obj === ‘object’); Use array.isarray () for arrays.

Transform data

Let’s first try writing a function that transforms objects:

Why write this function in the first place? Because transforming data is the most basic and important step, all subsequent steps will depend on this step.

/ / code 1.1
function defineReactive (obj,key,val) {
    Object.defineProperty(obj,key,{
        enumerable: true.configurable: true.get: function () {
            return val;
        },
        set: function (newVal) {
            // Determine whether the new value is equal to the old value
            // The second half of the judgment is to verify that NaN is not equal to itself if both the new value and the old value are NaN
            if(newVal === val || (newVal ! == newVal && value ! == value)){return; } val = newVal; }}); }Copy the code

Const obj = {} and call defineReactive(obj,’a’,2). Val =2 and obj. (Each call to defineReactive produces a closure that holds the value of val);

The process to discuss

After verification, it is found that this function does work. Then let’s discuss the flow of the response:

  1. The input data
  2. Modification data (defineReactive())
  3. If data changes => Events are triggered

Let’s look at step 3. How do data changes trigger subsequent events? If we want to change the data, we must first set the data, so we can just add methods to the set().

And then there’s the big question:

Depend on the collection

How do we know what event to trigger when the data changes? In the Vue:

Use data => View; When data is used to render the view, the best time to collect the dependencies is when the data is retrieved. Vue generates a Dep instance to collect the dependencies when the data attributes are modified.

/ / code 1.2
class Dep {
    constructor() {// Subscription information
        this.subs = [];
    }

    addSub(sub){
        this.subs.push(sub);
    }

    removeSub (sub) {
        remove(this.subs, sub);
    }

    // This method works the same as this.subs.push(Watcher);
    depend(){
        if (Dep.target) {
            Dep.target.addDep(this); }}// The method is to notify you of the change
    notify(){
        const subs = this.subs.slice()
        for (let i = 0, l = subs.length; i < l; i++) {
          subs[i].update();
        }
    }
}
Dep.target = null;Copy the code

Code 1.2 is part of the Dep code, and you only need to know what the two methods do for now

  • depend()— Can be understood as collecting dependent events, without considering other aspects of the function equivalentaddSub()
  • notify()— This method is more intuitive and executes all dependenciesupdate()Methods. It’s just changing the view later and so on.

This article focuses on the data response process, not the Watcher class, so you should know what the methods in Dep do.

Then there is the change to code 1.1

/ / code 1.3
function defineReactive (obj,key,val) {
    const dep = new Dep();

    Object.defineProperty(obj,key,{
        enumerable: true.configurable: true.get: function () {
            if(Dep.target){
                // Collect dependencies equivalent to dep.addSub(dep.target)
                dep.depend()
            }
            return val;
        },
        set: function (newVal) {
            if(newVal === val || (newVal ! == newVal && val ! == val)){return ;
            }
            val = newVal;
            // Publish changesdep.notify(); }}); }Copy the code

There is a puzzle in this code. What is dep.target? Why do you need a dep. target to collect dependencies?

  1. DepIs a class,Dep.targetIs an attribute of the class, it is notdepInstance properties.
  2. DepClass is available globally, soDep.targetIt is globally accessible and can change its value at will.
  3. getThis method is common enough that it cannot be called every time it is used to fetch a data valuedep.depend().
  4. dep.depend()In factdep.addSub(Dep.target).
  5. So the best thing to do is, before you use itDep.targetSet to an object after the subscription is completeDep.target = null.

validation

It’s time to verify the availability of a wave of code

/ / code 1.4

const obj = {};// initialize vue data ---- data:{obj:{}};

// Low matched watcher object can not be low matched (source code is a class, I use an object instead)
const watcher = {
    addDep:function (dep) {
        dep.addSub(this);
    },
    update:function(){ html(); }}// Pretend to render the page
function html () {
    document.querySelector('body').innerHTML = obj.html;
}
defineReactive(obj,'html'.'how are you');// Define reactive data

Dep.target = watcher;
html();// Render for the first time
Dep.target = null;Copy the code

The interface on the browser looks like this

Next open the console and start debugging by typing:

obj.html = 'I am fine thank you'Copy the code

And then, as soon as YOU hit Enter, it magically happens, and the page becomes

At the end

The design pattern for Vue data responses is a bit like the subscription publishing pattern, but different in that each DEP instance is a subscription hub, and each publication publishes all subscriptions. The reactive principle of Vue there are a large part of this paper mainly discusses the Vue is how to make data to respond, but in fact, the general data is a lot of, a data used by many, change the data after observation of new value, how to observe, how to subscribe to, how to schedule, still have a large part of the discussion. The three main classes Dep (collect dependencies), Observer (observe data), and Watcher (notify subscribers if data changes) are mentioned only a little.

Before writing a Vue response —- array variation method, for Vue array transformation is discussed. Of course, there will be more articles to come, and there will be much more to the whole data response process, but the three main classes have not been covered yet.

In fact, reading source code is not only to know how the source code works, but also to learn the author’s ideas and methods. I write articles that are not long. I hope I can focus on a point every time, and can truly understand the principle of this point. I also want to control the reading time so that people don’t turn it off in the middle.