Vue源码学习笔记(1)

Vue通过Object的defineProperty实现响应式,为data中的每个属性设置getter和setter,当属性值改变时,触发setter更新视图。依赖跟踪机制通过Dep类记录依赖关系,autorun函数在属性变化时重新执行相关函数,实现数据变化的实时响应。这一过程结合getter和setter,构成了Vue的迷你观察者模式。

Vue响应式原理

从原理上来讲vue是利用了Object的defineProperty的属性,它把我们放进去的data中每一个属性都设置了getter和setter,这样当data的属性值更新后,我们就会收到通知,并可以通知那些需要更新的地方去进行更新。

getter&&setter简单实现

//检查是否为对象
    function isObject (obj){
        return typeof obj === 'object'
        && !Array.isArray(obj)
        && obj !==null
        && obj !==undefined
    }

    function convert (obj){
        if(!isObject(obj)){
            throw  new TypeError()
        }
        //为每个对象的键值对添加getter和setter
        Object.keys(obj).forEach(key=>{
            //提供变量存储键对应的值
            let internalValue = obj[key]
            Object.defineProperty(obj,key,{
                get(){
                    console.log(`getting key "${key}": ${internalValue}`)
                    return internalValue
                },
                set(newValue){
                    console.log(`setting key "${key}" to: ${newValue}`)
                    internalValue = newValue
                }
            })
        })
    }

依赖跟踪

使用上面的getter和setter我们模拟了对象的响应式,那么问题来了,假设我们有一个对象,其中有个属性added:233,我们在一个函数中使用了这个added做一个加法,得到了一个结果,但是我们修改了added的值,并期望函数的结果也会随之改变,这要怎么实现呢?、

// a class representing a dependency
// exposing it on window is necessary for testing
window.Dep = class Dep {
    constructor () {
        //设置一个变量存储
      this.subscribers = new Set()
    }
    //跟踪函数依赖,类似于在此处插眼
    depend () {
      if (activeUpdate) {
        // register the current active update as a subscriber
          //将跟踪的函数加入到set表中
        this.subscribers.add(activeUpdate)
      }
    }
    //重新执行函数,在刚刚插眼的函数中重新执行该函数,达到更新的效果
    notify () {
      // run all subscriber functions
        //set列表中的每一个函数都重新执行
      this.subscribers.forEach(subscriber => subscriber())
    }
}

let activeUpdate

function autorun (update) {
    //用一个全局变量储存函数(重点!),这样可以让类实例在外部访问这个函数
  function wrappedUpdate () {
    activeUpdate = wrappedUpdate
    update()
    activeUpdate = null
  }
  wrappedUpdate()
}

简单来讲,就是创建一个类,它提供一种类似“插眼”的功能(depend ()),我们在需要跟踪的函数中插入这个眼,当我们需要它更新时,再调用这个眼(notify ())重新执行一遍函数,达到更新结果的目的。

迷你观察者

我们把依赖追踪和getter&&setter结合起来,就可以完成我们需要的功能了。

function isObject (obj) {
    return typeof obj === 'object'
      && !Array.isArray(obj)
      && obj !== null
      && obj !== undefined
}

function observe(obj) {
    if(!isObject(obj)){
        throw new TypeError()
    }

    Object.keys(obj).forEach(key=>{
        let internalValue = obj[key]
        let dep = new Dep()
        Object.defineProperty(obj,key,{
            get () {
                //插眼
                dep.depend()
                return internalValue
            },
            set(v) {
                let ifValueChange = v !==internalValue
                if(ifValueChange){
                    internalValue = v
                    //检测到数值变化重新调用该函数
                    dep.notify()
                }
            }
        })
    })
}
window.Dep = class Dep {
  constructor () {
    this.subscribers = new Set()
  }

  depend () {
    if (activeUpdate) {
      // register the current active update as a subscriber
      this.subscribers.add(activeUpdate)
    }
  }

  notify () {
    // run all subscriber functions
    this.subscribers.forEach(subscriber => subscriber())
  }
}

let activeUpdate

function autorun (update) {
  function wrappedUpdate () {
    activeUpdate = wrappedUpdate
    update()
    activeUpdate = null
  }
  wrappedUpdate()
}

可以看到我们把插眼这个动作放到了getter里面,把重新调用放到了setter里面,这样我们就可以监听对象的变化,一旦它的属性值发生改变,就会触发重新调用,这样就达到了监听对象,并实时更新与他有关的值,即Vue响应式的简单原理。

参考视频:尤雨溪教你写vue 高级vue教程 源码分析 中文字幕翻译完毕_哔哩哔哩_bilibili

参考github:GitHub - zhengguorong/vue-advanced-workshop: Vue.js workshop hosted by Evan You through Frontend Masters
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值