# Vue内部原理(四)| 小册免费学

简介: 与依赖收集相关的模块是:Dep实例负责维护属性的依赖列表,Watcher就是Dep实例维护的观察者队列中的观察者

1682512810(1).png

与依赖收集相关的模块是:Dep实例负责维护属性的依赖列表,Watcher就是Dep实例维护的观察者队列中的观察者

那么什么是依赖呢?用到数据的地方就是依赖。实际上这个依赖指的是watcher,收集依赖就是将watcher添加到dep的过程,依赖更新就是触发watcher的update

当watcher触发getter时,就把这个watcher收集到依赖中,数据发生变化时,就会通知这些watcher去更新

那么就先来看一下Watcher吧

export default class Watcher {
  constructor(target, expression, callback) {
    this.target = target;
    this.getter = parsePath(expression);
    this.callback = callback;
    this.value = this.get();
  }
  update() {
    this.run();
  }
  get() {
    //进入依赖收集阶段.让全局的Dep.target设置为Watcher本身,那么就是进入依赖收集阶段
    Dep.target = this;
    const obj = this.target;
    var value;
    //只要能找,就一直找
    try {
      value = this.getter(obj);
    } finally {
      Dep.target = null;
    }
    return value;
  }
  run() {
    this.getAndInvoke(this.callback)
  }
  getAndInvoke(cb) {
    const value = this.get();
    if (value !== this.value || typeof value == 'object') {
      const oldValue = this.value;
      cb.call(this.target, value, oldValue)
    }
  }
}
// 作用是将调用链解析出来,例如'a.b.c.d',就会从a:{b: {c: {d: value}}}中拿到value
function parsePath(str) {
  var segments = str.split('.');
  return (obj) => {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return;
      obj = obj[segments[i]]
    }
    return obj;
  }
}
复制代码

这里的Watcher也就是vue中的$watcher那个API,然后就是Dep

export default class Dep{
  constructor(){
    //用数组存储自己的订阅者。subs是subscribes订阅者的意思
    //这是数组里面放的是Watcher的实例
    this.subs = [];
  }
  //添加订阅
  addSub(sub){
    this.subs.push(sub)
  }
  //添加依赖
  depend(){
    //Dep.target我们自己指定的全局的位置,只要是全局唯一没有歧义就行
    if(Dep.target){
      this.addSub(Dep.target)
    }
  }
  //通知更新
  notify(){
    //浅克隆一份
    const subs = this.subs.slice();
    //遍历
    for(let i = 0,l = subs.length;i<l;i++){
      subs[i].update();
    }
  }
}
复制代码

注意Dep.target这个东西,这个东西非常巧妙,当watcher在实例化时会将Dep.target指向当前watcher,此时如果触发属性getter就会将当前watcher添加到dep中,利用闭包的原理,不同的对象属性维护着自己的dep实例

你现在应该还记得开始的defineReactive,我们在属性描述的getter中收集依赖(当试图读取属性时就会被记录),在setter中通知依赖更新

这时候defineReactive又要做出改变了

export default function defineReactive(data,key,val){
  const dep = new Dep();
  if(arguments.length == 2){
    val = data[key];
  }
  //子元素要进行observe,形成递归,多个函数循环调用
  let childOb = observe(val);
  Object.defineProperty(data,key,{
    enumerable:true,
    //可以被配置,比如可以被delete
    configurable:true,
    //getter
    get() {
      console.log(`打开${key}属性`)
      //如果现在处于依赖收集阶段
      if(Dep.target){
        dep.depend();
        if(childOb){
          childOb.dep.depend()
        };
      }
      return val;
    },
    //setter
    set(newValue) {
      if(val === newValue){
        return;
      }
      val = newValue;
      //当设置新值,这个新值也要被observe
      childOb = observe(newValue)
      //发布订阅模式,通知dep
      dep.notify();
    }
  });
}
复制代码

到现在为止,Vue的数据绑定原理已经分析完了,再来看一眼这张图片,是不是已经有所感悟了呢

1682512859(1).png

相关文章
|
1天前
|
监控 JavaScript
Vue中的数据变化监控与响应——深入理解Watchers
Vue中的数据变化监控与响应——深入理解Watchers
|
1天前
|
JavaScript 安全 前端开发
Vue 项目中的权限管理:让页面也学会说“你无权访问!
Vue 项目中的权限管理:让页面也学会说“你无权访问!
10 3
|
1天前
|
JavaScript 前端开发 开发者
Vue的神奇解锁:冒险的开始
Vue的神奇解锁:冒险的开始
5 1
|
2天前
|
JavaScript 前端开发
【vue】iview如何把input输入框和点击输入框之后的边框去掉
【vue】iview如何把input输入框和点击输入框之后的边框去掉
8 0
|
2天前
|
JavaScript
【vue实战】父子组件互相传值
【vue实战】父子组件互相传值
8 1
|
2天前
|
JavaScript
vue2_引入Ant design vue
vue2_引入Ant design vue
7 0
|
2天前
|
JavaScript
vue知识点
vue知识点
10 4
|
3天前
|
存储 JavaScript 前端开发
【Vue】绝了!这生命周期流程真...
【Vue】绝了!这生命周期流程真...
|
3天前
|
JavaScript 索引
【vue】框架搭建
【vue】框架搭建
7 1
|
3天前
|
JavaScript 前端开发 容器
< 每日小技巧: 基于Vue状态的过渡动画 - Transition 和 TransitionGroup>
Vue 的 `Transition` 和 `TransitionGroup` 是用于状态变化过渡和动画的组件。`Transition` 适用于单一元素或组件的进入和离开动画,而 `TransitionGroup` 用于 v-for 列表元素的增删改动画,支持 CSS 过渡和 JS 钩子。
< 每日小技巧: 基于Vue状态的过渡动画 - Transition 和 TransitionGroup>