Vue响应式原理

Obserber类:

作用

        数组,对象等转化成可观测数据

实现分析

  1. 创建Dep实例(每个Observe都有一个Dep实例)
  2. 标注数据已观测:给数据挂载__ob属性__
  3. 判断类型
    • 数组 
      • 数组原型上挂载重写的数组方法
      • 递归调用observe()
    • 对象  walk()中调用defineReactive(),将data中的键值转化为getter/setter形式
class Observer {
     constructor(v){
         // 每一个Observer实例身上都有一个Dep实例
         this.dep = new Dep()
        // 如果数据层次过多,需要递归去解析对象中的属性,依次增加set和get方法
        def(v,'__ob__',this)  //给数据挂上__ob__属性,表明已观测
        if(Array.isArray(v)) {
            // 把重写的数组方法重新挂在数组原型上
            v.__proto__ = arrayMethods
            // 如果数组里放的是对象,再进行监测
            this.observerArray(v)
        }else{
            // 非数组就直接调用defineReactive将数据定义成响应式对象
            this.walk(v)
        }
        
     }
     observerArray(value) {
         for(let i=0; i<value.length;i++) {
             observe(value[i])
         }
     }
     walk(data) {
         let keys = Object.keys(data); //获取对象key
         keys.forEach(key => {
            defineReactive(data,key,data[key]) // 定义响应式对象
         })
     }
 }

 function  defineReactive(data,key,value){
     const dep = new Dep() //实例化dep,用于收集依赖,通知订阅者更新
     observe(value) // 递归实现深度监测,注意性能
     Object.defineProperty(data,key,{
         configurable:true,
         enumerable:true,
         get(){
             // 如果现在处于依赖的收集阶段
             if(Dep.target) {
                 dep.depend()
             }
            //  依赖收集
            return value
         },
         set(newV) {
            if(newV === value) return
            //继续劫持newV,用户有可能设置的新值还是一个对象
            observe(newV)     
            value = newV
            console.log('值变化了:',value)
            // 发布订阅模式,通知
            dep.notify()
            // cb() //订阅者收到消息回调
         }
     })
 }

Dep类(订阅者)

实现分析

被observe的data在触发getter时,会:

  • 打上标记:Dep.target(在watcher类中实现),这里用到判断
  • 收集依赖:在subs队列当中添加watcher(只在实际页面当中用到的data数据,没用到可以不用做响应式处理)

当数据发生变动的时候,通过dep给watcher发通知更新:notify中调用watcher.update(),进行更新视图

var uid = 0
export default class Dep {
    constructor() {
        this.id = uid++
        this.subs = [] // subscribes订阅者,存储Watcher的实例
    }

    //收集watcher
    addSub(watcher) {
        this.subs.push(watcher)
    }
    //移除watcher
    removeSub(watcher) {
        remove(this.watcher, watcher)
    }
    // 收集依赖
    depend() {
        //全局唯一,实例化Watcher时会赋值Dep.target = Watcher实例
        if(Dep.target) {
            this.addSub(Dep.target)
        }
    }

    //通知watcher去更新
    notify() {
        console.log('通知观察者更新~')
        const subs = this.subs.slice() // 复制一份
        subs.forEach(w=>w.update())
    }
}

Watcher类(观察者)

实现分析

被observe的data在触发getter时,会:

  • 打上标记:Dep.target
  • 收集依赖:只在实际页面当中用到的data数据,没用到可以不用做响应式处理
var uid = 0
import {parsePath} from "../util/index"
import Dep from "./dep"
export default class Watcher{
    constructor(vm,expr,cb,options){
        this.vm = vm // 组件实例
        this.expr = expr // 需要观察的表达式
        this.cb = cb // 当被观察的表达式发生变化时的回调函数
        this.id = uid++ // 观察者实例对象的唯一标识
        this.options = options // 观察者选项
        this.getter = parsePath(expr)
        this.value = this.get()
    }

    get(){
        // 依赖收集,把全局的Dep.target设置为Watcher本身
        Dep.target = this
        const obj = this.vm
        let val
        // 只要能找就一直找
        try{
            val = this.getter(obj)
        } finally{
            // 依赖收集完需要将Dep.target设为null,防止后面重复添加依赖。
            Dep.target = null
        }
        return val
        
    }
    // 当依赖发生变化时,触发更新
    update() {
        this.run()
    }
    run() {
        this.getAndInvoke(this.cb)
    }
    getAndInvoke(cb) {
        let val = this.get()

        if(val !== this.value || typeof val == 'object') {
            const oldVal = this.value
            this.value = val
            cb.call(this.target,val, oldVal)
        }
    }
}

关系分析

 首先observer可以处理对象数组数据,将这些数据中添加getter和setter属性

其次在每个Observe当中,都有一个dep(私以为,observe中有很多个变量需要监控)

当数据的getter被调用,也就是页面用到这个数据的时候,我们在watcher类中将dep.target设置为watcher(也就是获得当前的环境,确定到底是哪个变量发生调用),之后在dep类将已经有dep.target内容的部分添加到dep.subs当中,其实也就是将调用到的watcher添加到subs队列当中,这样我们就可以知道并保存被用到的watcher

注意,在添加(收集依赖)完成之后,需要将target重新设置为null,方便下一个watcher指向

而当数据发生改变的时候,watcher类会触发update方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值