解读vuejs源码教程(一)

484 阅读2分钟

1、Object 数据监测

1、如何追踪变化 ?

vue 2.x 使用 Object.defineProperty vue 3.x 使用 Proxy

 function defineReaction(data, key, val) {
     Object.defineProperty(data, key, {
         enumerable: true,
         configurable: true,
         // 从data的key读取数据 get触发
         get: function () {
             return val
         },
         //从data的key设置数据 set 触发
         set: function (newValue) {
             if (val === newValue) {
                 return
             }
             val = newValue
         }
     })
 }

2、如何收集依赖 ?

//需要知道那些地方使用了数据 使用getter收集依赖
//数据更改时 向使用数据的地方通知数据改变 setter触发依赖
// 先通知到  数据对应的组件  组件内部 使用 虚拟dom 重新渲染
// 

3、收集依赖放在哪里?

// 放在数组 dep中 存储被收集的依赖 
 //收集依赖 
    export default class Dep {
        constructor() {
            this.subs = []
        }

        addSub(sub) {
            this.subs.push(sub)
        }
        removeSub(sub) {
            remove(this.subs, sub)
        }
        depend() {
            if (window.target) {
                this.addSub(window.target)
            }
        }
        notify() {
            const subs = this.subs.slice()
            for (let i = 0; l = subs.lengthli < l; i++) {
                subs[i].update()
            }
        }
    }

    function remove(arr, item) {
        if (arr.length) {
            const index = arr.indexOf(item)

            if (index > -1) {
                return arr.splice(index, 1)
            }
        }
    }
    // 有了 Dep类  改造一下之前的代码
    function defineReaction(data, key, val) {
        let dep = new Dep() //修改
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            // 从data的key读取数据 get触发
            get: function () {
                dep.depend() //修改
                return val
            },
            //从data的key设置数据 set 触发
            set: function (newValue) {
                if (val === newValue) {
                    return
                }
                val = newValue
                dep.notify() //新增
            }
        })
    }

2、收集

1、收集谁 ? Watcher

//先说明原理
先把自己设置到全局唯一指定位置 这里设置的是window.target,读取数据,
读取之后触发这个数据的getter,getter找到全局唯一指定位置读取正在读数据的
watcher,把这个watcher收集到 dep中,watcher实现主动订阅数据变化

// 首先说明 思想是这样的 将收集到的依赖封装到一个类的实例 
通知时 只通知这一个 再由它通知其他地方  
// 为什么要这样设计 ? 数据使用的地方很多 可能是模版也可能写在 watch中  

//简化模型  data.a.b.c属性改变时   触发第二个回调函数
vm.$watch('a.b.c',function(newVal,oldVal){
// 做点事情
})

// 将watcher实例 添加到data.a.b.c属性到Dep中就行   data.a.b.c值变化时 通知 watcher 再执行回调函数
export default class Watcher{
    constructor(vm,ex,cb){
        this.vm = vm
        // 执行 this.getter() 可读取data.a.b.c内容
        this.getter = parsePath(ex)
        this.cb = cb
        this.value = this.get()
    }

    get(){
        window.target = this
        let value =  this.getter.call(this.vm,this.vm)
        window.target  = undefined
        return value
    }

    update(){
        const oldValue = this.value
        this.value = this.get()
        this.cb.call(this.vm,this.value,oldValue)
    }
}

3、递归

1、貌似缺了东西,这个是监听某一个属性 ,我想实现监听所有的属性

//需要使用递归来监听所有的key
 // 递归侦测 所有key 
    export class Observer {
        constructor(value) {
            this.value = value;
            if (!Array.isArray(value)) {
                this.walk(value)
            }
        }
        // walk将每个属性转换成 getter/setter形式来侦测变化
        // 只有当数据类型为 Object时 被调用
        walk(obj) {
            const keys = Object.keys(obj)
            for (let i = 0; i < keys.length; i++) {
                defineReactive(obj, keys[i], obj[keys[i]])
            }
        }
    }

    function defineReaction(data, key, val) {
        //新增递归子属性
        if (typeof val === 'object') {
            new Observer(val)
        }
        let dep = new Dep() //修改
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            // 从data的key读取数据 get触发
            get: function () {
                dep.depend() //修改
                return val
            },
            //从data的key设置数据 set 触发
            set: function (newValue) {
                if (val === newValue) {
                    return
                }
                val = newValue
                dep.notify() //新增
            }
        })
    }

4、Object的问题

//添加属性 
//删除属性 也不能监测到

// 有没有 尝试想一下 为什么?

vue.js  Oject.defineProperty  将对象到key 转换成 
getter/setter形式追踪变化(等下 我们再展开说一下 这个事情)
,但是getter/setter只能追踪一个数据是否被修改,新增和删除无法追踪到(是不是就无解了?)

1、将对象到key 转换成 getter/setter形式追踪变化  回想之前到 get 和 set 函数 ,dep数组
再到后来到 watch 函数  是怎么起作用到

2、新增和删除无法追踪怎么解决?
 vuejs 未使用Proxy之前 有 vm.$set  vm.$delete 这两个API

本节完,希望有收获