vue的mvvm原理简单实现

1、实现原理图

 2、实现思路

三大模块:

  • 模板编译

主要简单处理两种:1指令编译 v-modle  2 文本编译 {{data}}

对应 更新元素数据(input) 方法    更新文本数据方法

  • 数据劫持

遍历传进来的数据对象,分别给对象的属性重写get \ set 方法

利用了Object.defineProperty

  • 观察者

将劫持和编译联系起来 ----》发布订阅

订阅: 将watcher 添加进数组中,
发布: 触发update方法时,一次遍历执行数组中的方法

3、代码实现

class MVVM {
  constructor(options) {
      /* 首先把可用的东西挂在实例上 */
      this.$el = options.el;
      this.$data = options.data;
      //如果有编译的模版就开始编译
      if(this.$el){
        //数据劫持,把对象的所有属性改为get 和 set
        new Observer(this.$data)
        this.proxyData(this.$data)
        //用数据和元素进行编译
        new Compile(this.$el,this);
      }
  }
    proxyData(data){
        Object.keys(data).forEach(key =>{
            Object.defineProperty(this,key,{
                get(){
                    return data[key]
                },
                set(newValue){
                    data[key] = newValue;
                }
            })
        })
    }
}

 其中,proxyData做了一个数据代理

当修改或访问数据时,vm.$data.message  改为直接访问 vm.message

上述核心代码主要实现两部分,一是数据劫持,二是数据编译

1.数据编译

 compileElement(node){
        //带v-model
        let attrs = node.attributes;
        var self = this;
        Array.from(attrs).forEach(function(attr){
            let attrName = attr.name;
            if(self.isDirective(attrName)){//attr.name attr.value;判断属性名字是否包含v-
                //取到对应的值放到节点上面
                let expr = attr.value;
                //node this.vm.$data
                //let [,type] = attrName.split("-")
                let type = attrName.slice(2);
                CompileUtil[type](node,self.vm,expr);
            }
        })
    }
    compileText(node){
        //带{{}}
        let expr = node.textContent;//取文本中的内容
        let reg = /\{\{([^}]+)\}\}/g;
        if(reg.test(expr)){
            //node this.vm.$data text
            CompileUtil['text'](node,this.vm,expr);
        }  
    }

2.数据劫持

//定义响应式
    defineReactive(data,key,value){
        var that = this;
        let dep = new Dep(); //每个变化的数据都会对应一个数组,这个数组是存放所有更新的操作
        Object.defineProperty(data,key,{
            enumerable:true,
            configurable:true,
            get(){//当取值时调用的方法
                Dep.target && dep.addSub(Dep.target)
                return value
            },
            set(newValue){//当给data属性中设置值的时候,更改获取的属性的值
                if(newValue!==value){
                    //这里的this并不是实例  vm.message = {b:1}
                    that.observe(newValue);//如果是对象,继续劫持
                    value = newValue;
                    dep.notify();//通知所有人,数据更新了
                }
            }
        })
    }

3、watcher

 get(){
        Dep.target = this;
        let value = this.getVal(this.vm,this.expr);
        Dep.target = null;
        return value
    }
    //对外暴露的方法
    update(){
        let newValue = this.getVal(this.vm,this.expr);
        let oldValue = this.value;
        if(newValue != oldValue){
            this.cb(newValue) //对应watch 的callback
        }
    }

watcher主要对比新值 和旧值,不一致则调用回调函数cb

​​​​​发布订阅

class Dep{
    constructor(){
        //订阅的数组
        this.subs = [];
    }
    addSub(watcher){
        this.subs.push(watcher);
    }
    notify(){
        this.subs.forEach(watcher =>watcher.update());
    }
}

 全部实现代码:https://github.com/wanghuixiago/mvvm

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值