关于Vue的源码解析(一)

先上一张vue底层原理关系图

vue底层原理关系图

上图完整的描述了 Vue 运行的机制,首先数据发生改变,就会经过 Data 处理,然后Dep会发出通知(notify),告诉 Watcher 有数据发生了变化,接着 Watcher 会传达给渲染函数跟他说有数据变化了,可以渲染视图了(数据驱动视图),进而渲染函数执行render 方法去更新 VNODE,也就是我们说的虚拟DOM,最后虚拟DOM根据最优算法,去局部更新需要渲染的视图。这里的 Data 就做了我们今天要说的事——数据劫持

一、数据的双向绑定

双向绑定数据响应的原理就是往data的属性调用Object.defineProperty方法去加get,set函数

    var obj = {};
    Object.defineProperty(obj,"name",{
      //Object.defineProperty(obj, prop, descriptor)
      // obj
      // 要在其上定义属性的对象。
      // prop
      // 要定义或修改的属性的名称。
      // descriptor
      // 将被定义或修改的属性描述符。
      get:function(){
        return document.querySelector('#name').innerHTML;
      },
      set:function(val){
        document.querySelector('#name').innerHTML = val;
      }
    })
    obj.name = "luccy"

那么知道vue的数据绑定原理之后,我们尝试下自己写一个vue.js叫KVue.js

创建一个类Kvue,接收的数据为
new Kvue({
data:{...}
})

class KVue{
  constructor(option) { //constructor 是一种用于创建和初始化class创建的对象的特殊方法。
    this.$options = option;

    // 数据响应化
    this.$data = option.data;  //获取data数据
    this.observe(this.$data);  //observe 监听方法
  }
}

observe方法用于监听data里面的数据变化

所以我们在KVued的类中需要创建一个observe方法

  // 监听
  observe(obj){
    if(!obj || typeof obj !== 'object'){  //判断书数据存在且是一个对象
      return;
    }

    // 遍历该对象
    Object.keys(obj).forEach(key =>{  //遍历拿出所有属性
      this.defineReactive(obj,key,obj[key]);  //数据响应化函数
    })
  }

该方法是通过遍历data里面的每一个值都加上get,set的监听即是vue双向绑定的原理

在KVue中创建defineReactive函数,这个方法叫做数据劫持

//  数据响应化
  defineReactive(obj,key,val){    // 可称为数据劫持

    this.observe(val);  //多层遍历,递归解决数据的嵌套

    Object.defineProperty(obj,key,{
      get(){
        return val;
      },
      set(newVal){
        if(newVal === val){
          return;
        }
        val = newVal;
        console.log(`${key}属性更新了:${val}`)
      }
    })
  }

这样就完成了数据响应化的操作即双向绑定

接下来在html中使用我们的KVue.js,并打开浏览器查看

  
    

image.png

二、依赖收集与追踪

找到页面中需要更新的地方,并通知更改

在KVue.js中创建Dep类(用于管理Watcher)和Watcher类(监听器负责更新视图)

// 依赖对象 用来管理Watcher
class Dep {
  constructor(){
    // 这里存放若干依赖(Watcher)
    this.deps = [];
  }
  // 添加依赖方法
  addDep(dep){
    this.deps.push(dep);
  }
  //通知方法  用于通知所有依赖去做更新
  notify(){
    this.deps.forEach(dep => dep.update())
  }
}
// 观察者,监听器:负责更新视图
class watcher {
  constructor(){
    // 将当期watcher实例指定到Dep静态属性target
    Dep.target = this;
  }
  // 更新操作
  update(){
    console.log('属性更新了');
  }
}

然后再模拟创建一下watcher
在KVue类中修改

constructor(option) { //constructor 是一种用于创建和初始化class创建的对象的特殊方法。
    this.$options = option;
    // 数据响应化
    this.$data = option.data;
    this.observe(this.$data);  //observe 监听方法
    
    //模拟watcher创建
    new watcher();
    this.$data.name = '雷神之斧2';
    new watcher();
    this.$data.list.litile = '大个子2'
  }

在遍历data的时候在defineReactive方法中添加依赖

get(){
        Dep.target && dep.addDep(Dep.target);  //添加依赖
        return val;
      },

打开浏览器会发现模拟watcher创建的属性读取了


运行

你可能感兴趣的:(关于Vue的源码解析(一))