0907-实现vue的响应式和事件的处理

// 函数作用域内的参数拿来用,get set暴露给外部,利用闭包原理;
function defineReactive(obj,key,val) {
    // 嵌套对象需要递归遍历
    observe(val);
    const dep = new Dep();

    Object.defineProperty(obj,key,{
        get(){
            console.log(`get ${key}`)
            Dep.target && dep.addDep(Dep.target)
            return val;
        },
        set(newValue){
            console.log(`set ${key}`)
            if (newValue!==val){
                // 新值如果是对象,则要对它执行响应式处理
                observe(newValue)
                val = newValue

                 // 通知更新
                dep.notify()
            }
        }
    })
}

function observe(obj) {
        // 判断传入的值是不是对象
        if (typeof obj !== 'object' || obj === null) {
            return obj
        }
        // 创建Observe 实例
        new Observer(obj);

}
class Observer {
    constructor(value){
        this.obj = value;
        // 判断value数值是不是数组
        if (Array.isArray(obj)){
            // 1.找到数组的原型
            const originProto = Array.prototype;
            // 辈分一份 修改备份
            const arrayProto = Object.create(originProto)
            ['push','pop','shift','unshift','splice','reverse','sort'].forEach(method=>{
                arrayProto[method] =function(){
                    originProto[method].apply(this, arguments)

                }
            })
            obj.__proto__ = arrayProto;
            let obj = Object.keys(obj)
            for (let index = 0; index < obj.length; index++) {
                defineReactive(obj,index,obj[index])
            }
            //todo
        }else {
            this.walk(value);
        }
    }
    walk(obj) {
        Object.keys(obj).forEach(key=>{
            defineReactive(obj, key, obj[key])
        })
    }
}

class KVue{
    constructor(options){
        this.$options = options;
        this.$data = options.data;

        // 对data 做响应式处理
        observe(this.$data)

        // 需要把data 代理到vm上 可以直接this.xxx
        proxy(this)

        // 执行模板编译
        new Compile(options.el,this)
    }
}
function proxy(vm){
    Object.keys(vm.$data).forEach(key=>{
        // 这样设置可以把data 挂载到vm上并且是响应式
        Object.defineProperty(vm,key,{
            get(){
                return vm.$data[key]
            },
            set(val){
                vm.$data[key] = val
            }
        })
    })
}

class Compile {
    constructor(el,vm){
        this.$el = document.querySelector(el);
        this.$vm = vm;

        // 判断元素节点是不是存在
        if(this.$el) {
            this.compile(this.$el)
        }
    }

    //
    compile(el) {
        // 获取所有的孩子节点
        const childNodes = el.childNodes;

        childNodes.forEach(node => {

            // 1是元素节点
            if (node.nodeType ===1){

                // 执行编译节点上的属性
                this.compileElement(node)
        
                // 当发现node上还有子节点 进行递归遍历
                if (node.hasChildNodes){
                    this.compile(node)
                }
            }else if (node.nodeType===3 && /\{\{(.*)\}\}/.test(node.textContent)) {
                // 文本节点 {{XXX}}
                this.compileText(node, node.textContent)
            }

        })
    }
    compileElement(node) {
        // 元素节点处理属性
        let nodeAttrs = node.attributes;
        Array.from(nodeAttrs).forEach(attr =>{
            // v-text ='xxx'
            const attrName = attr.name; //v-text
            const exp = attr.value; // xxx

            // 判断属性是不是k-开头
            if (attrName.indexOf('k-')===0) {
                // 指令 每个指令有特定的处理函数
                const dir = attrName.substring(2);
                this[dir] && this[dir](node, exp)


            }else if(attrName.indexOf('@')===0){
                // 事件
                const name = attrName.substring(1);
                const fn  =this.$vm.$options.methods && this.$vm.$options.methods[exp]
                node.addEventListener(name, fn.bind(this.$vm))
                
            }

        })
    }
    updata(node,exp, dir) {
        // 首先初始化值 
        const  fn = this[dir+'Updater'];
        // this.$vm[exp] 表示实例上的具体数值
        fn&& fn(node, this.$vm[exp])

        // 页面上有一个解析vue的就需要创建一个watcher
        // data 里面的key
        new Watcher(this.$vm,exp ,val=>{
            fn && fn(node, val);
        }
    )}
    textUpdater(node, value){
        node.textContent = value
    }
    htmlUpdater(node, value) {
        node.innerHTML = value;
    }
    modelUpdater(node,value){
        node.value = value;
    }
    text(node, exp){
        this.updata(node, exp,'text')
    }
    html(node,exp) {
        this.updata(node,exp,'html')
    }
    model(node,exp) {
        this.updata(node,exp,'model')

        node.addEventListener('input',(e)=>{
            this.$vm[exp] = e.target.value;
        })
    }
    compileText(node){
        this.updata(node, RegExp.$1, 'text')
    }
}
class Watcher {
    constructor(vm,key,updater){
        this.vm = vm;
        this.key = key;
        this.updateFn = updater 

        // 把当前实例挂载到Dep.target上
        Dep.target =this;

        // 触发get
        vm[key]

        Dep.target = null;
    }

    // 未来执行dom更新函数,由dep调用的
  update() {
    this.updateFn.call(this.vm, this.vm[this.key])
  }

}
class Dep{
    constructor(){
        this.deps = []
    }
    addDep(watcher){
        this.deps.push(watcher)
    }
    notify() {
        this.deps.forEach(watcher=>watcher.update())
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值