vue3.0 双向绑定原理,VUE双向绑定原理

  vue3.0 双向绑定原理,VUE双向绑定原理

  本文主要介绍Vue2.x Vue利用Object.defineProperty()方法劫持数据的双向绑定原理,利用set和get检测数据读写。有需要的朋友可以参考下面这篇文章的具体内容。

  

目录

   1、实现流程2、显示一个Observer3、实现Watcher4、实现Compile5、添加解析事件6。myVueVue完整版是通过使用Object.defineProperty()方法,使用set和get来检测数据读写的数据劫持。

  https://jsrun.net/RMIKp/embedded/all/light

  MVVM框架主要包括两个方面:数据变化更新视图,视图变化更新数据。

  查看更改更新数据。如果是类似输入的标签,可以使用oninput事件。

  数据更改可用于更新视图。Object.definProperty()的set方法可以检测数据变化。当数据发生变化时,该功能将被触发,然后视图将被更新。

  

1、实现过程

  我们知道如何实现双向绑定。首先,我们需要劫持和监控数据,所以我们需要设置一个观察者函数来监控所有属性的变化。

  如果属性发生了变化,应该通知订阅者观察器查看数据是否需要更新。如果有多个订阅者,则需要一个Dep来收集这些订阅者,然后以统一的方式管理监听器观察器和观察器。

  您还需要一个指令解析器来扫描和解析需要监控的节点和属性。

  因此,流程大概是这样的:

  实现一个监听器观察器,用于劫持和监听所有属性,并在有变化时通知订阅者。

  实现一个订阅者观察器,当收到属性改变的通知时,执行相应的函数,然后更新视图,使用Dep收集这些观察器。

  实现一个解析器编译,用于扫描和解析节点的相关指令,并根据初始化模板初始化相应的订阅者。

  

2、显示一个 Observer

  Observer是一个数据监听器,其核心方法是使用Object.defineProperty()递归添加setter和getter方法到所有属性中进行监听。

  var库={

  第一册:{

  名称: ,

  },

  第二本书: ,

  };

  观察(图书馆);

  Library.book1.name=vue权威指南;//属性名已被监控,现在其值为:“vue权威指南”

  Library.book2=没有这样的书;//属性book2已经被监控,现在它的值是:“没有这个书”

  //添加数据检测

  函数定义有效(数据,键,值){

  观察(val);//递归遍历所有子属性

  let Dep=new Dep();//创建新的dep

  Object.defineProperty(数据,键,{

  可枚举:真,

  可配置:真,

  get: function() {

  if (Dep.target) {

  //确定是否需要添加订户。只需要第一次添加,之后就不需要了。详情请参见观察器功能。

  dep . add sub(dep . target);//添加订户

  }

  返回val

  },

  set: function(newVal) {

  if (val==newVal)返回;//如果值没有改变,则返回

  val=newVal

  console.log(

  属性“key”已被监视,现在它的值是:“ newVal.toString()”

  );

  dep . notify();//如果数据发生变化,通知所有订阅服务器。

  },

  });

  }

  //侦听对象的所有属性

  功能观察(数据){

  如果(!数据数据类型!==object) {

  返回;//如果不是对象,则返回

  }

  Object.keys(数据)。forEach(函数(键){

  defineReactive(data,key,data[key]);

  });

  }

  //Dep负责收集订阅者,并在属性发生变化时触发更新功能。

  函数Dep() {

  this . subs={ };

  }

  部门原型={

  addSub: function(sub) {

  this . subs . push(sub);

  },

  通知:函数(){

  this . subs . foreach((sub)=sub . update());

  },

  };

  在分析中,需要有一个可以容纳订阅者的消息订阅者Dep,用于收集订阅者,并在属性发生变化时执行相应的更新功能。

  从代码的角度来看,订阅者Dep被添加到getter中,以便在观察器初始化时触发。因此,有必要确定是否需要该订户。

  在setter中,如果任何数据发生变化,所有订阅者都会得到通知,然后订阅者会更新相应的函数。

  至此,一个相对完整的观测器已经完成。接下来,我们开始设计观察器。

  

3、实现 Watcher

  订户观察器需要在初始化期间将其自身添加到订户Dep中。我们已经知道listener Observer在get期间执行Watcher操作,所以我们只需要在Watcher初始化时触发相应的get函数来添加相应的subscriber操作。

  在这里,如何触发get?因为我们已经设置了Object.defineProperty(),所以只需要获取相应的属性值就可以触发它了。

  我们只需要在初始化订阅者观察器时在Dep.target上缓存订阅者,然后在成功添加后删除它们。

  函数观察器(vm,exp,cb) {

  this.cb=cb

  this.vm=vm

  this.exp=exp

  this . value=this . get();//将您自己添加到订阅者的操作

  }

  Watcher.prototype={

  更新:函数(){

  this . run();

  },

  run: function() {

  var value=this . VM . data[this . exp];

  var oldVal=this.value

  如果(值!==oldVal) {

  this.value=value

  this.cb.call(this.vm,value,old val);

  }

  },

  get: function() {

  Dep.target=this//缓存自己,用来决定是否添加watcher。

  var value=this . VM . data[this . exp];//在侦听器中强制执行get函数

  Dep.target=null//释放自己

  返回值;

  },

  };

  到目前为止,已经设计了简单的观察器,然后通过将观察器与观察器相关联,可以实现简单的双向绑定。

  因为解析器编译还没设计好,可以先把模板数据写死。

  将代码写入ES6构造函数,预览一下。

  https://jsrun.net/8SIKp/embed.

  这段代码直接传入绑定变量,因为它没有实现编译器。我们只在一个节点上设置一个数据(名称)进行绑定,然后在页面上做new MyVue,就可以实现双向绑定了。

  而两秒后,就值得换了。如您所见,页面也发生了变化。

  //MyVue

  proxyKeys(键){

  var self=this

  Object.defineProperty(this,key,{

  可枚举:false,

  可配置:真,

  get:函数proxyGetter() {

  return self . data[key];

  },

  set:函数proxySetter(newVal) {

  self . data[key]=new val;

  }

  });

  }

  上面代码的作用是将this.data的键代理到this,这样我就可以很方便的用this.xx得到this.data.xx

  

4、实现 Compile

  虽然上面实现了双向数据绑定,但是整个过程并不解析DOM section store,而是固定和替换的,所以下一步要实现一个解析器来解析和绑定数据。

  解析器 compile 的实现步骤:

  解析模板指令,替换模板数据,并初始化视图。

  将相应的更新函数绑定到模板的指定节点,并初始化相应的订阅者。

  为了解析模板,我们需要首先解析DOM数据,然后在DOM元素上处理相应的指令。所以整个DOM操作比较频繁,我们可以创建一个新的片段,把要解析的DOM存储到片段中进行处理。

  函数节点到分段(el) {

  var fragment=document . createdocumentfragment();

  var child=el.firstChild

  while (child) {

  //将Dom元素移动到片段中

  fragment.appendChild(子);

  child=el.firstChild

  }

  返回片段;

  }

  接下来,我们需要遍历每个节点,对包含相关指令和模板语法的节点进行特殊处理。首先,我们需要执行最简单的模板语法处理,并使用“{{variable}}”语法的常规解析。

  函数编译元素(el) {

  var child nodes=El . child nodes;

  var self=this

  [].slice.call(childNodes)。forEach(函数(节点){

  var reg=/\{\{(。*)\}\}/;//匹配{{xx}}

  var text=node.textContent

  如果(自我。istext节点(节点)寄存器。test(text)){//确定它是否是符合此形式的指令{{}}

  self.compileText(node,reg . exec(text)[1]);

  }

  if(node . child nodes node . child nodes . length){

  self.compileElement(节点);//继续递归遍历子节点

  }

  });

  },

  函数compileText (node,exp) {

  var self=this

  var init text=this . VM[exp];

  updateText(node,initText);//初始化视图中的初始化数据

  Newwatcher (this.vm,exp,function(value){//生成订阅者并绑定更新函数。

  self.updateText(节点,值);

  });

  },

  函数updateText(节点,值){

  node.textContent=typeof value==未定义?“”:值;

  }

  获得最外层节点后,调用compileElement函数判断所有子节点。如果节点是文本节点切匹配{{}}形式的指令,编译并初始化相应的参数。

  然后需要为当前参数生成对应的更新函数订阅者,当数据发生变化时更新对应的DOM。

  这样就完成了解析、初始化、编译三个过程。

  接下来,可以修改myVue,使用模板变量进行双向数据绑定。

  https://jsrun.net/K4IKp/embed.

  

5、添加解析事件

  添加Compile后,基本完成了一个数据的双向绑定。下一步是添加更多的指令进行编译,比如v-model、v-on、v-bind等。

  添加一个 v-model 和 v-on 解析:

  函数编译(节点){

  var nodeAttrs=node.attributes

  var self=this

  array . prototype . foreach . call(nodeAttrs,function(attr) {

  var attrName=attr.name

  if(is direct(attrName)){

  var exp=属性值;

  var dir=attrname . substring(2);

  if(iseventdirection(dir)){

  //事件指令

  self.compileEvent(node,self.vm,exp,dir);

  }否则{

  //垂直模型指令

  self.compileModel(node,self.vm,exp,dir);

  }

  node.removeAttribute(属性名);//解析后,移除该属性。

  }

  });

  }

  //垂直指令解析

  函数是直接的(属性){

  return attr . index of( v-)==0;

  }

  //on:指令解析

  函数iseventdirection(dir){

  return dir . index of( on:)===0;

  }

  上面的编译函数是用来遍历当前dom的所有节点属性,然后判断属性是否是指令属性,如果是则做相应的处理(事件会听事件,数据会听数据.)

  

6、完整版 myVue

  在MyVue中添加mounted方法,该方法将在所有操作完成后执行。

  MyVue类{

  构造函数(选项){

  var self=this

  this . data=options . data;

  this . methods=options . methods;

  Object.keys(this.data)。forEach(函数(键){

  self.proxyKeys(键);

  });

  观察(this . data);

  新编译(options.el,this);

  options . mounted . call(this);//一切处理完毕后执行挂载的函数。

  }

  proxyKeys(键){

  //将this.data属性代理到此

  var self=this

  Object.defineProperty(this,key,{

  可枚举:false,

  可配置:真,

  get:函数getter() {

  return self . data[key];

  },

  set:函数setter(newVal) {

  self . data[key]=new val;

  },

  });

  }

  }

  然后就可以测试使用了。

  https://jsrun.net/Y4IKp/embed.

  总结一下过程,回头看看这张图。是不是清楚多了?

  可以查看的代码地址:Vue2.x双向绑定的原理与实现

  关于Vue2.x双向绑定的原理和实现的文章到此为止,更多关于Vue数据双向绑定的原理,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: