Maison >interface Web >js tutoriel >Explication détaillée du principe de réactivité de vue.js (avec code)

Explication détaillée du principe de réactivité de vue.js (avec code)

不言
不言avant
2018-11-16 16:52:361606parcourir

Cet article vous apporte une explication détaillée du principe de réactivité de vue.js (avec code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

J'ai été exposé à angularjs il y a longtemps, j'avais déjà compris qu'angularjs implémentait la surveillance des données et le rendu des mises à jour des pages via une vérification sale. Après cela, je suis entré en contact avec vue.js à cette époque, j'étais très curieux de savoir comment vue.js surveillait les mises à jour des données et restituait la page. Aujourd'hui, nous allons analyser étape par étape le principe de réactivité de vue.js et implémenter une démo simple.

Tout d’abord, comprenons quelques bases.

Connaissances de base

Object.defineProperty

es5 a ajouté l'API Object.defineProperty, qui nous permet de définir des getters et des setters pour les propriétés des objets, afin que nous puissions détourner l'acquisition de valeur par l'utilisateur et l'attribution de propriétés d'objet. Par exemple, le code suivant :

const obj = {
};

let val = 'cjg';
Object.defineProperty(obj, 'name', {
  get() {
    console.log('劫持了你的取值操作啦');
    return val;
  },
  set(newVal) {
    console.log('劫持了你的赋值操作啦');
    val = newVal;
  }
});

console.log(obj.name);
obj.name = 'cwc';
console.log(obj.name);

Nous avons détourné les opérations d'acquisition et d'affectation de valeur de obj[name] via Object.defineProperty, nous pouvons donc faire quelques astuces ici, par exemple, nous pouvons faire quelque chose dans. obj[ name] reçoit une valeur, l'opération de mise à jour de la page est déclenchée.

Modèle de publication-abonnement

Le modèle de publication-abonnement est l'un des modèles de conception les plus courants, dans lequel il existe deux rôles : éditeur et abonné. Plusieurs abonnés peuvent s'abonner à un événement du même éditeur. Lorsque l'événement se produit, l'éditeur informe tous les abonnés abonnés à l'événement. Regardons un exemple pour comprendre.

class Dep {
  constructor() {
    this.subs = [];
  }
  // 增加订阅者
  addSub(sub) {
    if (this.subs.indexOf(sub) < 0) {
      this.subs.push(sub);
    }
  }
  // 通知订阅者
  notify() {
    this.subs.forEach((sub) => {
      sub.update();
    })
  }
}

const dep = new Dep();

const sub = {
  update() {
    console.log('sub1 update')
  }
}

const sub1 = {
  update() {
    console.log('sub2 update');
  }
}

dep.addSub(sub);
dep.addSub(sub1);
dep.notify(); // 通知订阅者事件发生,触发他们的更新函数

Pratique pratique

Après avoir compris Object.defineProperty et le modèle de publication-abonnement, nous pouvons facilement imaginer que vue.js implémente la surveillance des données basée sur les deux ci-dessus.

  1. vue.js utilise d'abord Object.defineProperty pour détourner les getters et setters des données à surveiller Lorsque les propriétés des données sont attribuées/récupérées, vue.js peut détecter leur arrivée. et gérer en conséquence.

  2. Grâce au modèle de publication par abonnement, nous pouvons créer un éditeur pour chaque attribut de l'objet lorsque d'autres abonnés dépendent de cet attribut, l'abonné sera ajouté à la publication dans la file d'attente. . Grâce au détournement de données d'Object.defineProperty, lorsque le setter de la propriété est appelé, l'éditeur de la propriété informe tous les abonnés du contenu mis à jour.

Ensuite, implémentons-le (voir les commentaires pour plus de détails) :

class Observer {
  constructor(data) {
    // 如果不是对象,则返回
    if (!data || typeof data !== 'object') {
      return;
    }
    this.data = data;
    this.walk();
  }

  // 对传入的数据进行数据劫持
  walk() {
    for (let key in this.data) {
      this.defineReactive(this.data, key, this.data[key]);
    }
  }
  // 创建当前属性的一个发布实例,使用Object.defineProperty来对当前属性进行数据劫持。
  defineReactive(obj, key, val) {
    // 创建当前属性的发布者
    const dep = new Dep();
    /*
    * 递归对子属性的值进行数据劫持,比如说对以下数据
    * let data = {
    *   name: 'cjg',
    *   obj: {
    *     name: 'zht',
    *     age: 22,
    *     obj: {
    *       name: 'cjg',
    *       age: 22,
    *     }
    *   },
    * };
    * 我们先对data最外层的name和obj进行数据劫持,之后再对obj对象的子属性obj.name,obj.age, obj.obj进行数据劫持,层层递归下去,直到所有的数据都完成了数据劫持工作。
    */
    new Observer(val);
    Object.defineProperty(obj, key, {
      get() {
        // 若当前有对该属性的依赖项,则将其加入到发布者的订阅者队列里
        if (Dep.target) {
          dep.addSub(Dep.target);
        }
        return val;
      },
      set(newVal) {
        if (val === newVal) {
          return;
        }
        val = newVal;
        new Observer(newVal);
        dep.notify();
      }
    })
  }
}

// 发布者,将依赖该属性的watcher都加入subs数组,当该属性改变的时候,则调用所有依赖该属性的watcher的更新函数,触发更新。
class Dep {
  constructor() {
    this.subs = [];
  }

  addSub(sub) {
    if (this.subs.indexOf(sub) < 0) {
      this.subs.push(sub);
    }
  }

  notify() {
    this.subs.forEach((sub) => {
      sub.update();
    })
  }
}

Dep.target = null;

// 观察者
class Watcher {
  /**
   *Creates an instance of Watcher.
   * @param {*} vm
   * @param {*} keys
   * @param {*} updateCb
   * @memberof Watcher
   */
  constructor(vm, keys, updateCb) {
    this.vm = vm;
    this.keys = keys;
    this.updateCb = updateCb;
    this.value = null;
    this.get();
  }

  // 根据vm和keys获取到最新的观察值
  get() {
    Dep.target = this;
    const keys = this.keys.split('.');
    let value = this.vm;
    keys.forEach(_key => {
      value = value[_key];
    });
    this.value = value;
    Dep.target = null;
    return this.value;
  }

  update() {
    const oldValue = this.value;
    const newValue = this.get();
    if (oldValue !== newValue) {
      this.updateCb(oldValue, newValue);
    }
  }
}

let data = {
  name: 'cjg',
  obj: {
    name: 'zht',
  },
};

new Observer(data);
// 监听data对象的name属性,当data.name发现变化的时候,触发cb函数
new Watcher(data, 'name', (oldValue, newValue) => {
  console.log(oldValue, newValue);
})

data.name = 'zht';

// 监听data对象的obj.name属性,当data.obj.name发现变化的时候,触发cb函数
new Watcher(data, 'obj.name', (oldValue, newValue) => {
  console.log(oldValue, newValue);
})

data.obj.name = 'cwc';
data.obj.name = 'dmh';

Conclusion

De cette façon, un simple suivi des données réactif est complet. Bien sûr, il ne s'agit que d'une simple démo pour illustrer le principe de réactivité de vue.js. Le vrai code source de vue.js sera plus compliqué car de nombreuses autres logiques sont ajoutées.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer