vue响应式原理代码

数据劫持结合订阅者观察者模式,通过节点的改变,将对应的收集对应的观察者,去触发对应的节点更新

let Global=null;
const utils = {
    getValue(value,vm){
        return vm.$data[value.trim()]
    },
    setValue(value,vm,newValue){
        vm.$data[value] = newValue
    },
    model(node,value,vm){
        const initValue = this.getValue(value,vm)
        console.log(initValue)
        new Watcher(value,vm,(newValue)=>{
            this.modelUpdate(node,newValue)
        })
        node.addEventListener('input',(e)=>{
            const newValue = e.target.value
            this.setValue(value,vm,newValue)
        })
        this.modelUpdate(node,initValue)
    },
    modelUpdate(node,newVal){
        node.value=newVal
    },
    text(node,value,vm){
        // console.log(node,value,vm)
        let result;
        if(value.includes('{{')){
            result = value.replace(/\{\{(.+?)\}\}/g,(...args)=>{
                const expr = args[1]
                new Watcher(expr,vm,(newVal)=>{
                    this.textUpdate(node,newVal)
                })
                return this.getValue(args[1],vm)
            })
        }else{
            result = this.getValue(value,vm)
        }
        this.textUpdate(node,result)
    },
    on(node,value,vm,event){
        console.log(vm,'vm')
        const fn = vm.options.methods[value]
        node.addEventListener(event,fn.bind(vm),false)
    },
    textUpdate(node,newValue){
        node.textContent = newValue
    }
}

// 收集dom的依赖
class Watcher{
    constructor(expr,vm,cb){
        this.expr = expr;
        this.vm = vm;
        this.cb = cb;
        // 通过getter对数据进行绑定,标记当前的watcher
        this.oldValue = this.getOldValue();
    }
    getOldValue(){
        Global = this
        const oldValue = utils.getValue(this.expr,this.vm)
        Global = null
        return oldValue
    }
    update(){
        const newValue = utils.getValue(this.expr,this.vm)
        if(newValue!==this.oldValue){
            this.cb(newValue)
        }
    }
}

// 把我们的数据跟多个Watcher绑定起来
class Dep{
    constructor(){
        this.collect = []
    }
    addwatcher(watcher){
        this.collect.push(watcher)
    }
    notify(){
        this.collect.forEach(w=>w.update())
    }
}
class Observe{
    constructor(data){
        this.observe(data)
    }
    observe(data){
        if(typeof data ==='object'){
            Object.keys(data).forEach(key=>{
                this.defineReactive(data,key,data[key])
            })
        }
    }
    defineReactive(obj,key,value){
        this.observe(value)
        const dep = new Dep()
        Object.defineProperty(obj,key,{
            get(){
                const target = Global
                Global && dep.addwatcher(target)
                // console.log('get',key,value)
                return value
            },
            set:(newVal)=>{
                console.log('set',newVal)
                if(newVal===value) return;
                this.observe(newVal)
                value = newVal
                dep.notify()
            }
        })
    }
}

class Compiler{
    constructor(el,vm){
        this.el = this.isElementNode(el) ? el:document.querySelector(el)
        this.vm = vm

        const fragment = this.compileFragment(this.el)
        this.compiler(fragment)
        this.el.appendChild(fragment)
    }

    compiler(fragment){
        const childNodes = Array.from(fragment.childNodes)
        // console.log(childNodes)
        childNodes.forEach(childNode=>{
            if(this.isElementNode(childNode)){
                // console.log('标签',childNode)
                this.compilerElement(childNode)
            }else if(this.isTextNode(childNode)){
                this.compileText(childNode)
                // console.log('文本',childNode)
            }
            if(childNode.childNodes&&childNode.childNodes.length){
                this.compiler(childNode)
            }
        })
    }

    // 解析文本节点
    compileText(node){
        const text = node.textContent
        if(/\{\{(.+)\}\}/.test(text)){
            utils['text'](node,text,this.vm)
            console.log('text',text)
        }
    }

    // 解析标签节点
    compilerElement(node){
        const attributes = Array.from(node.attributes)
        attributes.forEach(attr=>{
            const { name,value } = attr
            // console.log('attr',name,value)
            if(this.isDirector(name)){
                // 指令 v-for ;v-on:click ;v-bind ;v-if ;v-model
                const [ , directorName ] = name.split('-')
                const [ compileKey , eventName ] = directorName.split(':')
                // console.log(directorName,compileKey,eventName,'123')
                utils[compileKey](node,value,this.vm,eventName)
            }else if(this.isEvent(name)){
                const [,eventName] = name.split('@')
                utils['on'](node,value,this.vm,eventName)
            }
        })
    }
    isDirector(name){
        return name.startsWith('v-')
    }
    isEvent(name){
        return name.startsWith('@')
    }
    isTextNode(el){
        return el.nodeType === 3
    }

    // 创建文档节点
    compileFragment(el){
        const f = document.createDocumentFragment(el)
        // console.log(f,'f')
        let firstChild;
        // console.log(el,el.firstChild,'el')
        while(firstChild=el.firstChild){
            f.appendChild(firstChild)
        }
        return f;
        // console.log(f,'f')
    }

    isElementNode(el){
        return el.nodeType===1
    }
}
// Vue类
class Vue{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;
        this.options = options

        // 将data绑定到vue实例上面,并触发this.$data.xxx与模板的绑定
        new Observe(this.$data)

        // 解析模板
        new Compiler(this.$el,this)

        this.ProxyData(this.$data)

    }

    ProxyData(data){
        Object.keys(data).forEach(key=>{
            Object.defineProperty(this,key,{
                get(){
                    console.log('获取:key',key,data[key])
                    return data[key]
                },
                set(newVal){
                    data[key] = newVal
                }
            })
        })
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值