【vue源码系列】深入理解Vue源码,持续更新中~【一】


function defineReactive(data, key, val) {
    // let dep = []; //收集依赖
    // if (typeof val === 'object') {
    //     new Observer(val)
    // }
    let childOb = observe(val) //修改
    let dep = new Dep() //修改
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            // window.target 为全局唯一依赖
            // dep.push(window.target)
            dep.depend() //添加依赖
            // 这里手机Array的依赖
            if (childOb) {
                childOb.dep.depend()
            }
            return val
        },
        set: function (newVal) {
            if (val === newVal) {
                return
            }
            // 循环触发依赖
            // for (let i = 0; i < dep.length; i++) {
            //     dep[i](newVal, val)
            // }
            val = newVal;
            dep.notify() // 调用dep类的notify,循环通知
        }
    })
}

// 封装Dep类 创建Dep用来收集依赖,删除依赖和向依赖发消息等操作 而所谓的依赖就是下面封装的Watcher类
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.length; i < 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)
        }
    }
}

// window.target 抽象 
// eg:
// vm.$watch('a.b.c', function (newVal, oldVal) {
// //someThing
// })

/**
 * 依赖
 * 所谓依赖就是Watcher,只有Watcher触发的getter才会收集依赖,那个Watcher出发了getter,就把那个watcher收集到dep中,当数据发生变化时,会循环依赖列表,把所有的依赖(wathcer)都通知一遍
 * 原理是把自己設置到全局唯一的制定位置,例如window.taeget,然后读取数据。因为读取了数据 所以触发这个数据的getter,接着就会在getter从全局唯一制定的位置读取当前正在读取数据的Watcher并收集到dep中。
 * 通过这种方式可以主动去订阅任意一个数据的变化。
 * */
export default class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm
        // 执行this.getter(),就可以读取data.a.b.c的内容
        this.getter = parsePath(expOrFn)
        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)
    }
}
/**
 * 解析简单路径
 **/
const bailRE = /[^\w.$]/

function parsePath(path) {
    if (bailRE.test(path)) {
        return
    }
    const segments = path.split('.')
    return function (obj) {
        for (let i = 0; i < segments.length; i++) {
            if (!obj) return
            obj = obj[segments[i]]
        }
        return obj;
    }
}

// 工具函数
function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
    })
}

// 递归侦测所有Key
/**
 * Observer 类会附加到每一个被侦测的Object上
 * 一旦被附加上,Observer会将object的所有属性转换为getter/setter的形式
 * 未收集属性的依赖,并且当属性发生变化时会通知这些依赖
 *
 * 作用把一个object中所有的数据转换为响应式的。
 * 侦测一个Object中所有的数据包括子数据的变化,
 * 因为ES6之前没有元编程的能力,所以删除和新增属性都无法被侦测到,后续的Vue3会使用proxy重写
 * */
// __proto__是否可用
const hasProto = '__proto__' in {}
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)

export class Observer {
    constructor(value) {
        this.value = value
        this.dep = new Dep(); //新增Dep
        def(value, '__ob__', this) //新增

        if (Array.isArray(value)) {
            this.observeArray(value)
        } else {
            this.walk(value)
        }
        if (Array.isArray(value)) {
            // value.__proto__ = arrayMethods // 新增
            const augment = hasProto ? protoAugment : copyAugment
            augment(value, arrayMethods, arrayKeys)
        } else {
            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]])
        }
    }

    /**
     * 侦测Array中的每一项
     */
    observeArray(items) {
        for (let i = 0, l = items.length; i < l; i++) {
            observe(items[i])
        }
    }

}

function protoAugment(target, src, keys) {
    target.__proto__ = src
}

function copyAugment(target, src, keys) {
    for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i]
        def(target, key, src[key])
    }
}

// 数组的变化侦听
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
    //缓存原始方法
    const original = arrayProto[method]
    // Object.defineProperty(arrayMethods, method, {
    //     value: function mutator(...args) {
    //         const ob = this.__ob__ // 新增 获取Observer实例
    //         return original.apply(this, args)
    //     },
    //     enumerable: false,
    //     writable: true,
    //     configurable: true
    // })
    def(arrayMethods, method, function mutator(...args) {
        const result = original.apply(this, args)
        const ob = this.__ob__ // 新增 获取Observer实例
        let inserted
        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.slice(2)
                break;
        }
        if (inserted) ob.observeArray(inserted)
        ob.dep.notify() // 向依赖发送消息
        return result
    })
})

/**
 * 尝试为value创建一个Observer实例,
 * 如果创建成功,直接返回新创建的Observer实例。
 * 如果value已经存在一个Observer实例,则直接返回它
 * */
export function observe(value, asRootData) {
    if (!isObject(value)) {
        return
    }
    let ob
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
    } else {
        ob = new Observer(value)
    }
    return ob
}

var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn (obj, key) {
    return hasOwnProperty.call(obj, key)
}

声明:麋鹿与鲸鱼|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 【vue源码系列】深入理解Vue源码,持续更新中~【一】


Carpe Diem and Do what I like