Le principe de liaison bidirectionnelle des données Vue est obtenu grâce au détournement de données combiné au modèle « éditeur-abonné ». Tout d'abord, les données sont surveillées, puis l'abonné est averti lorsque les propriétés surveillées changent. pour mettre à jour, si mis à jour, la fonction de mise à jour correspondante sera exécutée pour mettre à jour la vue.
Le principe de la liaison bidirectionnelle des données Vue est réalisé grâce au détournement de données combiné au modèle éditeur-abonné. Tout d'abord, les données sont surveillées, puis quand. Lorsque les propriétés surveillées changent, il est indiqué à l'abonné s'il doit être mis à jour. En cas de mise à jour, la fonction de mise à jour correspondante sera exécutée pour mettre à jour la vue
[Cours recommandé : Tutoriel Vue]
Mode MVC
Le mode MVC précédent était une liaison unidirectionnelle, c'est-à-dire le modèle était lié à la vue Lorsque nous mettons à jour le modèle avec le code JavaScript, la vue sera automatiquement mise à jour
Mode MVVM
MVVM. Le mode est le mode Modèle – Vue – ViewModel. Il se rend compte que les modifications apportées à View sont automatiquement reflétées dans ViewModel et vice versa. La compréhension de la liaison bidirectionnelle est que lorsque l'utilisateur met à jour la vue, les données du modèle sont automatiquement mises à jour. Il s'agit d'une situation de liaison bidirectionnelle. Pour être plus détaillé, sur la base d'une liaison unidirectionnelle, un événement de changement (input) est ajouté aux éléments d'entrée input, textare, etc. (l'événement de changement est déclenché et l'état de la vue est mis à jour) pour dynamiquement modifier le modèle.
Principe de liaison bidirectionnelle
La liaison bidirectionnelle des données Vue s'effectue par détournement de données combiné au modèle éditeur-abonné. Implémenté
Nous savons déjà que pour implémenter la liaison bidirectionnelle des données, nous devons d'abord détourner et surveiller les données, nous devons donc configurer un observateur pour surveiller toutes les propriétés. Si l'attribut change, vous devez indiquer à l'abonné Watcher pour voir s'il doit être mis à jour. Parce qu'il y a de nombreux abonnés, nous avons besoin d'un service d'abonnés aux messages pour collecter spécifiquement ces abonnés, puis les gérer de manière uniforme entre l'Observateur et l'Observateur. Ensuite, nous avons également besoin d'un analyseur d'instructions Compile pour analyser et analyser chaque élément de nœud, initialiser les instructions pertinentes (telles que v-model, v-on) dans un observateur d'abonné et remplacer les données du modèle ou les lier à la fonction correspondante, lorsque le l'abonné Watcher reçoit la modification de l'attribut correspondant, il exécutera la fonction de mise à jour correspondante pour mettre à jour la vue.
Nous effectuons ensuite les 3 étapes suivantes pour obtenir une liaison bidirectionnelle des données :
(1) Implémentez un observateur d'écoute pour détourner et surveiller tous les attributs. S'il y a des changements, informez les abonnés. .
(2) Implémentez un observateur abonné. Chaque observateur est lié à une fonction de mise à jour. L'observateur peut recevoir des notifications de changement de propriété et exécuter les fonctions correspondantes pour mettre à jour la vue.
(3) Implémentez un analyseur Compile qui peut analyser et analyser les instructions pertinentes de chaque nœud (v-model, v-on et autres instructions si le nœud a v-model, v-on et autres). instructions, L'analyseur Compile initialise les données du modèle de ce type de nœud afin qu'elles puissent être affichées sur la vue, puis initialise l'abonné correspondant (Watcher).
Implémentation d'un observateur
Observer est un écouteur de données et sa méthode d'implémentation principale est Object.defineProperty(). Si vous souhaitez surveiller toutes les propriétés, vous pouvez parcourir toutes les valeurs de propriétépar récursion et les traiter avec Object.defineProperty()
Le code suivant implémente un observateur.
function Observer(data) { this.data = data; this.walk(data); } Observer.prototype = { walk: function(data) { var self = this; //这里是通过对一个对象进行遍历,对这个对象的所有属性都进行监听 Object.keys(data).forEach(function(key) { self.defineReactive(data, key, data[key]); }); }, defineReactive: function(data, key, val) { var dep = new Dep(); // 递归遍历所有子属性 var childObj = observe(val); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function getter () { if (Dep.target) { // 在这里添加一个订阅者 console.log(Dep.target) dep.addSub(Dep.target); } return val; }, // setter,如果对一个对象属性值改变,就会触发setter中的dep.notify(), 通知watcher(订阅者)数据变更,执行对应订阅者的更新函数,来更新视图。 set: function setter (newVal) { if (newVal === val) { return; } val = newVal; // 新的值是object的话,进行监听 childObj = observe(newVal); dep.notify(); } }); } };function observe(value, vm) { if (!value || typeof value !== 'object') { return; } return new Observer(value); };// 消息订阅器Dep,订阅器Dep主要负责收集订阅者,然后在属性变化的时候执行对应订阅者的更新函数 function Dep () { this.subs = []; } Dep.prototype = { /** * [订阅器添加订阅者] * @param {[Watcher]} sub [订阅者] */ addSub: function(sub) { this.subs.push(sub); }, // 通知订阅者数据变更 notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } }; Dep.target = null;
Dans Observer, quand j'ai regardé le code source d'autres personnes, une chose que je n'ai pas comprise, c'est d'où Dep.target
venait. Je pense que certaines personnes auront les mêmes doutes que moi. Ne vous inquiétez pas ici. Lorsque vous écrivez sur Watcher, vous constaterez que ceci Dep.target
vient de Watcher.
Implémentation d'un Watcher
Watcher est abonné. Utilisé pour traiter le message de mise à jour envoyé par l'Observer et exécuter la fonction de mise à jour liée au Watcher.
Le code suivant implémente un Watcher
function Watcher(vm, exp, cb) { this.cb = cb; this.vm = vm; this.exp = exp; this.value = this.get(); // 将自己添加到订阅器的操作} Watcher.prototype = { update: function() { this.run(); }, run: function() { var value = this.vm.data[this.exp]; var oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); } }, get: function() { Dep.target = this; // 缓存自己 var value = this.vm.data[this.exp] // 强制执行监听器里的get函数 Dep.target = null; // 释放自己 return value; } };
Dans le processus d'étude du code, j'ai senti que le plus compliqué était de comprendre les paramètres de ces fonctions plus tard, après ma sortie. ces paramètres, ces fonctions de fonctions sont également faciles à comprendre. vm est l'objet SelfValue qui sera écrit plus tard, ce qui équivaut à un objet du nouveau Vue dans Vue. exp est la valeur d'attribut du modèle v du nœud ou v-on:click
et d'autres instructions.
Comme vous pouvez le voir dans le code ci-dessus, dans la fonction getter de Watcher, Dep.target
pointe vers lui-même, qui est l'objet Watcher. Dans la fonction getter,
var value = this.vm.data[this.exp] // 强制执行监听器里的get函数。 这里获取vm.data[this.exp] 时,会调用Observer中Object.defineProperty中的get函数 get: function getter () { if (Dep.target) { // 在这里添加一个订阅者 console.log(Dep.target) dep.addSub(Dep.target); } return val; },
ajoute ainsi l'observateur à l'abonné, ce qui résout le problème de savoir d'où vient ce Dep.target
ci-dessus.
Implémenter une compilation
Compile主要的作用是把new SelfVue 绑定的dom节点,(也就是el标签绑定的id)遍历该节点的所有子节点,找出其中所有的v-指令和" {{}} ".
(1)如果子节点含有v-指令,即是元素节点,则对这个元素添加监听事件。(如果是v-on,则node.addEventListener('click')
,如果是v-model,则node.addEventListener('input'))
。接着初始化模板元素,创建一个Watcher绑定这个元素节点。
(2)如果子节点是文本节点,即" {{ data }} ",则用正则表达式取出" {{ data }} "中的data,然后var initText = this.vm[exp]
,用initText去替代其中的data。
实现一个MVVM
可以说MVVM是Observer,Compile以及Watcher的“boss”了,他需要安排给Observer,Compile以及Watche做的事情如下
(1)Observer实现对MVVM自身model数据劫持,监听数据的属性变更,并在变动时进行notify
(2)Compile实现指令解析,初始化视图,并订阅数据变化,绑定好更新函数
(3)Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update。
最后,把这个MVVM抽象出来,就是vue中Vue的构造函数了,可以构造出一个vue实例。
最后写一个html测试一下我们的功能
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>self-vue</title></head><style> #app { text-align: center; }</style><body> <div id="app"> <h2>{{title}}</h2> <input v-model="name"> <h1>{{name}}</h1> <button v-on:click="clickMe">click me!</button> </div></body><script src="js/observer.js"></script> <script src="js/watcher.js"></script> <script src="js/compile.js"></script> <script src="js/mvvm.js"></script> <script type="text/javascript"> var app = new SelfVue({ el: '#app', data: { title: 'hello world', name: 'canfoo' }, methods: { clickMe: function () { this.title = 'hello world'; } }, mounted: function () { window.setTimeout(() => { this.title = '你好'; }, 1000); } });</script></html>
先执行mvvm中的new SelfVue(...),在mvvm.js中,
observe(this.data); new Compile(options.el, this);
先初始化一个监听器Observer,用于监听该对象data属性的值。
然后初始化一个解析器Compile,绑定这个节点,并解析其中的v-," {{}} "指令,(每一个指令对应一个Watcher)并初始化模板数据以及初始化相应的订阅者,并把订阅者添加到订阅器中(Dep)。这样就实现双向绑定了。
如果v-model绑定的元素,
<input v-model="name">
即输入框的值发生变化,就会触发Compile中的
node.addEventListener('input', function(e) { var newValue = e.target.value; if (val === newValue) { return; } self.vm[exp] = newValue; val = newValue; });
self.vm[exp] = newValue;
这个语句会触发mvvm中SelfValue的setter,以及触发Observer对该对象name属性的监听,即Observer中的Object.defineProperty()
中的setter。setter中有通知订阅者的函数dep.notify
,Watcher收到通知后就会执行绑定的更新函数。
最后的最后就是效果图啦:
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!