MVVM原理

MVVM 双向数据绑定

  1. 脏值检查

AngularJS 实现方式

  1. 数据劫持+发布订阅模式

VUE 的实现方式(不兼容低版本 Object.defineProperty)

  • Object.defineProperty()
let obj = {};
Object.defineProperty(obj, "school", {
  configurable: true,
  // writable:true,
  enumerable: true,
  // value:'hhhh',
  get() {
    //获取obj.school值时会调用get
    return "hhhh";
  },
  set(val) {
    //给obj赋值时会调用set
    console.log(val);
  }
});
  • 发布-订阅模式 订阅[fn1,fn2,fn3]
//发布订阅
function Dep(){
    this.subs=[];
}
Dep.prototype.addSub=function(sub){//订阅
    this.subs.push(sub);
};
Dep.prototype.notify=function(){
    this.subs.forEach(sub=>sub.update());
};
function Watcher(fn){//Watcher是个类,通过这个类创建的实例都拥有update方法
    this.fn=fn;
}
Watcher.prototype.update=function () {
    this.fn();
};
let watcher=new Watcher(function () { //监听函数
   console.log(1)
 });
 let dep =new Dep();
 dep.addSub(watcher);//将watcher放到了数组中[watcher.update]
 console.log(dep.subs);
 dep.notify();//数组关系
  • 代码实现
<div id="app">
  <p>a的值{{a.a}}</p>
  <div>b的值{{b}}</div>
  <input type="text" v-model="b" />
  <!-- computed可以缓存 只是把数据挂在vm上 -->
  {{hello}}
</div>
function Vue(options = {}) {
  this.$options = opthins; //将所有属性挂载在了$options
  // this._data
  var data = (this._data = this.$options.data);
  observe(data);
  //this代理了this._data
  for (let key in data) {
    Object.defineProperty(this, key, {
      enumerable: true,
      get() {
        return this._data[key]; //this.a={a:1}
      },
      set(newVal) {
        this._data[key] = newVal;
      }
    });
  }
  initComputed.call(this);
  new Compile(options.el, this);
}
function initComputed() {//具有缓存功能的
    let vm=this;
    let computed=this.$options.computed;//Object.keys {name:1,age:2}=>[name,age]
    Object.keys(computed).forEach(function (key) {
        Object.defineProperty(vm,key,{//computed[key]
            get:typeof computed[key]==='function'?computed[key]:computed[key].get,
            set(){

            }
        })
    })
}
function Compile(el, vm) {
  // el表示替换的范围
  vm.$el = document.querySelector(el);
  let fragment = document.createDocumentFragment();
  while ((child = vm.$el.firstChild)) {
    //将app中的内容移入到内存中
    fragment.appendChild(child);
  }
  replace(fragment);
  function replace(fragment) {
    Array.from(fragment.childNodes).forEach(function(node) {
      //循环每一层
      let text = node.textContent;
      let reg = /\{\{(.*)\}\}/;
      if (node.nodeType === 3 && reg.test(text)) {
        console.log(RegExp.$1);//vm.a.a vm.b
        let arr=RegExp.$1.split('.');//[a,a]
        let val=vm;
        arr.forEach(function (k) {//取this.a.a this.b
            val=val[k];
        });
        // 替换的逻辑
        new Watcher(vm,RegExp.$1,function (newVal) { //函数里需要接收一个新的值
            node.textContent=text.replace(/\{\{(.*)\}\}/,newVal);
         });
        node.textContent=text.replace(/\{\{(.*)\}\}/,val);
      }
      if(node.nodeType===1){
        //   元素节点
        let nodeAttrs=node.attributes;//获取当前dom节点的属性
        Array.from(node.attributes).forEach(function (attr) {
            let name=attr.name;//type="text"
            let exp=attr.value;//v-model="b"
            if(name.indexOf('v-')==0){//v-model
                node.value=vm[exp];
            }
            new Watcher(vm,exp,function (newVal) {
                node.value=newVal;//当watcher触发时会自动将内容放到输入框内
            });
            node.addEventListener('input',function (e) {
                let target=e.target.value;
                vm[exp]=newVal;
              })

          })


      }
      if (node.childNodes) {
          replace(node);
      }
    });
  }
  vm.$el.appendChild(fragment);
}
// vm.$options
//观察对象给对象增加Object.defineProperty
function Observe(data) {
  //主要逻辑
  let dep=new Dep();
  for (let key in data) {
    let val = data[key];
    observe(val);
    //把data属性通过object.defineProperty的方式定义属性
    Object.defineProperty(data, key, {
      enumerable: true,
      get() {
          Dep.target&&dep.addSub(Dep.target);//[watcher]
          return val;
      },
      set(newValue) {
        //更改值的时候
        if (newValue === val) {
          return;
        }
        val = newValue; //如果以后在获取值的时候将刚才设置的值在丢回去
        observe(newValue);
        dep.notify();//让所有的watch的update方法执行
      }
    });
  }
}
function observe(data) {
  if (typeof data != "object") return;
  return new Observe(data);
}
//vue 不能新增不存在的属性,因为不存在的属性没有 get 和 set
// 深度响应 因为每次赋予一个新对象时会给这个对象增加数据劫持

//发布订阅
function Dep(){
    this.subs=[];
}
Dep.prototype.addSub=function(sub){//订阅
    this.subs.push(sub);
};
Dep.prototype.notify=function(){
    this.subs.forEach(sub=>sub.update());
};
// watcher
function Watcher(vm,exp,fn){//Watcher是个类,通过这个类创建的实例都拥有update方法
    this.fn=fn;
    this.vm=vm;
    this.exp=exp;//添加到订阅中
    Dep.target=this;
    let val=vm;
    let arr=exp.split('.');
    arr.forEach(function (k) {//this.a.a
        val=val[k];
    })
    Dep.target=null
}
Watcher.prototype.update=function () {
    let val=this.vm;
    let arr=this.exp.split('.');
    arr.forEach(function (k) {//this.a.a
        val=val[k];
    })
    this.fn(val);//newVal
};

let vue = new Vue({
  el: "#app",
  //data: { a: 1 };
  //data: { a: { a: 1 } }
  data: { a: { a: "a" }, b: "b" },
   computed: {
      hello(){
          return this.b+this.c
      }
  },
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MVVM是Model-View-ViewModel的简写,它是MVC的改进版。MVVM分为三个部分,即模型层(Model)、视图层(View)和连接桥梁(ViewModel)。模型层主要负责业务数据相关,视图层负责展示视图,而ViewModel作为连接桥梁,负责监听模型和视图的变化,实现双向绑定的功能。 使用MVVM的好处在于将视图的状态和行为抽象化,实现视图UI与业务逻辑的分离。MVVM支持双向绑定,当模型层数据发生修改时,ViewModel会察觉变化并通知视图层进行相应的修改;反之,当视图层发生修改时,ViewModel也会通知模型层进行数据的修改,实现视图与模型层的解耦。 关于Vue中的MVVM实现原理,具体可以参考Vue的源码实现。根据引用中的代码,可以看出,在Vue中,创建一个MVVM实例时,会将数据对象传入,并通过observe方法进行监听。同时,Vue还会创建一个Compile实例来编译模板,并将MVVM实例作为参数传入。然后,通过数据劫持和模板编译的机制,最终实现了MVVM的双向绑定功能。 总结来说,Vue的MVVM原理主要是通过数据劫持和模板编译来实现的,数据劫持负责监听数据的变化,模板编译负责将数据和视图进行绑定。这样就实现了数据的双向绑定,当数据变化时,视图会自动更新,并且当视图发生变化时,数据也会相应地更新。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值