Vue—12—框架的一些原理比如响应式等;


 

vue是如何实现响应式的?

首先通过object.defineProperty方法获取实时改变的值;

然后通过订阅者模式将值推送给所有用到此值的属性上;

主要是发布者订阅者模式;

 

 

 

1是通过defineProperty方法可以实时知道属性值的改变,

2发布者通过调用notify方法,notify方法又会使用数组元素即订阅者的update方法来更新值

 

 

在new  vue实例时,在数据还没发生改变时,

  1. 首先到了complie这一步,我们开始渲染el所绑定的html模板,然后发现有{{}}语法,
  2. 然后会将本{{}}的id标识为一个订阅者,并new一个watcher实例(id为初始化时的形参),在new类watcher时,构造方法里会调用本身的update方法,update方法会使用object.defineProperty方法里的get方法将data里的初始值绑定到里面
  3. 但是get方法里有一个dep.addSubs(watcher)的方法,所以watcher即订阅者被加入到了dep的数组中;

在数据发生改变时,

  1. 首先通过obj.defineProperty实时监听改变了属性,并将新值赋值给属性
  2. 其次通过dep对象里,保存的订阅者数组subs,通过数组subs里的每个元素(订阅者)的notify()方法,将所有的属性的新值推送给用到了这个属性的{{}}id;

 

 

 

响应式原理:coderwhy:

简单说:

当我们new一个vue实例时,

数据劫持: 上面的observer:data数据会交给observer,为了实现响应式系统,vue会定义一个defineProperty方法,来重写data中每个属性(msg、name)的getters和setters,这样达到了数据劫持的目的,再在getters中给每个data的属性new一个dep发布者对象,

依赖收集:下面的compile:当解析到mustanche语法时,会根据mustanche语法创建一个wactcher,标记为订阅者,并加入到dep对象中;

当数据发生改变时:

defineProperty方法监听到数据发生了改变其实就是setters方法被使用,于是调用dep对象的notify方法,notify方法里每个订阅者又会调用自身的update方法完成视图的更新;

 详细说:

第一阶段:数据劫持

在我们初始化vue实例的时候,data里面的属性会被添加到observer类中做数据劫持,也就是说使用defineReactive方法给data里面的每个属性初始化一个dep实例作为发布者,并使用Object.defineProperty方法给每个属性重写setter和getter方法;

其中getters方法会在watcher实例不为null时,将watcher添加到dep实例的数组中,即保存订阅者;

其中setters方法会现有一个判断是否新旧值相等,如果不相等,将新值newVal赋值到旧值val,然后调用notify方法;

至于为社么将新值newVal赋值到旧值val而不是直接新值作为参数传给notify方法,原因就是watcher在修改node节点值的时候,实际调用的还是getter方法,而getter只能返回val,所以要将newVal赋值给val;

 

第二阶段:挂载组件(也可以叫注册watcher阶段、依赖收集阶段)

获取dom树div节点下的所有节点,目前只通过firstChild方法获取了第一个节点,然后通过正则表达式将符合{{}}语法的文本节点取出并将node、文本节点的nodevalue、vue实例都作为参数去初始化一个watcher实例;

而初始化watcher的时候,会使用update方法触发getters方法获取vue实例的name属性的值,获取到之后将值赋值给node的nodevalule,然后使用

createDocumentFragment()方法创建虚拟dom,并将子节点添加到虚拟dom上,再将虚拟dom添加到div节点下;于是乎组件就被渲染起来了;
第三阶段:更新数据

defineProperty方法监听到数据发生了改变其实就是setters方法被使用,于是调用dep对象的notify方法,notify方法里每个订阅者又会调用自身的update方法完成视图的更新;

 

 

 

 

 这个defineProperty方法,有两个功能:(应该就一个功能,即数据劫持,实时监测说白了就是调用了setters呗)

  1. 可以实时监测对象属性的变化,(使用setters)
  2.  还有一个功能就是数据劫持:我们重写了这相当于我们自定义了obj.msg属性和name属性的取值和赋值的行为,使用自定义的gettersetter来重写了原有的行为,这也就是数据劫持的含义。

 

当我们需要取值的时候,比如obj.msg是通过我们的getters方法才可以取到值得,所以我们在getters方法里得return至关重要;

 

当我们重写了getters和setters方法时,所有的obj.key或者obj【key】都将走我们得getters方法,如果我们的getters里return 1,那么当我们obj.msg得时候不会等于diy,而是等于1,而这就是数据劫持;

我一直想着不要去定义let value = obj【key】,直接再getters里return obj.key岂不更简单,但是却一致返回的是undefied,你知道为什么吗?

因为当我们返回obj.key时,这不是一个确定的值,这需要去obj得key属性得值,那么怎么取呢?竟然要用我们自己定义的getters方法取(自己调用自己,递归!),可我们自己定义的getters方法又只会返回一个obj.key,于是陷入了一个死循环;

 

 

 

 

 

 

 

 

 

 

响应式原理文章:

第一阶段:数据劫持,所有data属性里的属性都会被加上自定义的setget来重写它以前的setget

第二阶段:依赖收集或者加挂载阶段,会自动啊new一个watcher函数,并将vm即vue实例和updateComponent作为参数传递进去。

然后在new的过程中,会执行render函数去取值并显示在页面上,然后取值的过程中触发了getters,于是乎所有用到这个属性的watcher都将被加到这个属性所对应的dep中,完成依赖收集。

第三阶段:在settters里notify

 

总的来说,数据劫持定义好之后,先触发getters完成原始数据显示并完成依赖收集;

然后修改数据时触发setters,setters会调用notify方法完成数据的推送;

 

posted @ 2021-09-06 10:46  Eric-Shen  阅读(132)  评论(0编辑  收藏  举报