深入浅出Vue.js(一) 变化侦测

变化侦测真是很难理解啊,还是需要结合书去看去学习
 
//dep.js

let uid = 0

export class Dep {
    constructor(){
        this.id = uid++
        this.subs = []
    }
    addSub(sub){
        this.subs.push(sub)
    }
    removeSub(sub){
        const index = this.subs.indexOf(sub)
        if(index > -1){
            return this.subs.splice(index,1)
        }
    }
    depend(){
        if(window.target){
            window.target.addDep(this)
        }
    }
    notify(){
        const subs = this.subs.slice()
        for(let i = 0,len = subs.length;i < len;i++){
            subs[i].update()
        }
    }
}

  

//observer.js

mport Dep from "./dep";

function observe(value){
    if(!isObject(value)){
        return
    }
    let ob
    if(hasOwn(value,'__ob__') && value.__ob__ instanceof Observer){
        ob = value.__ob__
    }else{
        ob = new Observer(value)
    }
    return ob
}

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

const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const baseMethods = ['push','pop','shift','unshift','splice','sort','reverse']
baseMethods.forEach(method => {
    // 缓存原始方法
    const original = arrayProto[method]
    def(arrayMethods,method,function mutator(...args){
        const result = original.apply(this,args)
        const ob = this.__ob__
        let inserted
        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.splice(2)
            default:
                break;
        }
        if(inserted){
            ob.observeArray(inserted)
        }
        ob.dep.notify()
        return result
    })
})

function defineReactive(data,key,val){
    let childOb = observe(val)
    let dep = new Dep()
    Object.defineProperty(data,key,{
        enumerable:true,
        configurable:true,
        get(){
            dep.depend()
            if(childOb){
                childOb.dep.depend()
            }
            return val
        },
        set(newVal){
            if(val === newVal){
                return
            }
            val = newVal
            dep.notify()
        }
    })
}

export class Observer {
    constructor(value){
        this.value = value
        this.dep = new Dep()
        def(value,'__ob__',this)
        if(Array.isArray(value)){
            this.observeArray(value)
        }else{
            this.walk(value)
        }
    }
    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,len = items.length;i < len;i++){
            observe(items[i])
        }
    }
}

  

//watcher.js

const reg = /[^\w.$]/

function parsePath(path){
    if(reg.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
    }
}

const seenObjects = new Set()

function traverse(value){
    _traverse(value,seenObjects)
    seenObjects.clear()
}

function _traverse(val,seen){
    let i,keys
    const isArray = Array.isArray(val)
    if((!isArray && !isObject(val)) || Object.isFrozen(val)){
        return
    }
    if(val.__ob__){
        const depId = val.__ob__.dep.id
        if(seen.has(depId)){
            return
        }
        seen.add(depId)
    }
    if(isArray){
        i = val.length
        while(i--) _traverse(val[i],seen)
    }else{
        keys = Object.keys(val)
        i = keys.length
        while(i--) _traverse(val[keys[i]],seen)
    }
}

export class Watcher {
    constructor(vm,expOrFn,cb,options){
        this.vm = vm
        if(options){
            this.deep = !!options.deep
        }else{
            this.deep = false
        }
        this.deps = []
        this.depIds = new Set()
        if(typeof expOrFn === 'function'){
            this.getter = expOrFn
        }else{
            this.getter = parsePath(expOrFn)
        }
        this.cb = cb
        this.value = this.get()
    }
    get(){
        window.target = this
        let value = this.getter.call(this.vm,this.vm)
        if(this.deep){
            traverse(value)
        }
        window.target = undefined
        return value
    }
    update(){
        const oldValue = this.value
        this.value = this.get()
        this.cb.call(this.vm,this.value,oldValue)
    }
    addDep(dep){
        const id = dep.id
        if(!this.depIds.has(id)){
            this.depIds.add(id)
            this.deps.push(dep)
            dep.addSub(this)
        }
    }
    teardown(){
        let len = this.deps.length
        while(len--){
            this.deps[i].removeSub(this)
        }
    }
}

  

posted @ 2020-06-10 16:17  671_MrSix  阅读(179)  评论(0编辑  收藏  举报