双向数据绑定响应式原理乞丐版

vue1.0数据双向绑定乞丐版

Object.defineProperty

Object.defineProperty是js独有api,vue主要依赖他实现的mvvm模式;

mvvm模式:m代表模型,v视图,vm就是视图模型;

Object.defineProperty(obj, prop,object )
  1. obj是需要定义得当前得对象
  2. prop是当前需要定义属性名
  3. object是属性描述符

一般对象赋值也可以删除修改,但是Object.defineProperty可以更加精准得控制对象

object对象里面有5个属性,可以对属性进行控制:

  1. value: 给当前需要定义属性赋值

  2. writable:跟布尔值,true表示可以修改,false表示只读,修改无效,可以赋值修改,控制台不会报错

  3. configurable:跟布尔值,false表示不能再次使用defineProperty或者赋值修改,控制台报错;不能删除这个属性,不报错

   delete obj.b   // 删除对象某个属性
  1. enumerable:跟布尔值,false表示当前属性不能使用for in循环遍历,其他属性可以

  2. get:他是一个函数,当访问对象当前属性会被触发

  3. set:他是一个函数,当设置对象当前属性会被触发

var obj = {a:1,b:2}
    Object.defineProperty(obj,'b',{
      value:123,
      writable:false,
      configurable:false,
      get:()=>console.log('访问'),
      set:()=>console.log('设置')
    })

如何实现数据变了,视图也变?我在什么时间点修改了那个属性

劫持的对象不能存储改变得新值,必须重新创建对象newObj,劫持他,从而改变obj的值

<div>1</div>
<div>2</div>

var obj = {a:1,b:2,c:3}
    var newObj = {}  //set数据存储得地方
      function observe(){
        for(let key in obj){
      console.log(key); // a b c
      Object.defineProperty(newObj,key,{
        set:function(newVal){
          console.log(`有人修改了${key},新值是${newVal}`)
          obj[key]=newVal  //把修改值存在newObj里
            //更新视图
          document.getElementById('a1').innerHTML=1*obj.a
          document.getElementById('a1').innerHTML=2*obj.a
        },
        get:function(){
          console.log(`有人修改了${key}`);
          return obj[key]  //访问newObj[key]会把obj[key]  return出去
        }
      })
    }
   }
      observe()
   newObj.a  //可以访问修改

如何实现数据变了,多处视图跟着变,观察者模式

观察者模式:最常见的23种设计模式之一,他定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个对象,当这个目标对象发生变化时,会通知所有观察者。

订阅者模式:不是特别严谨情况他们指同一种东西

function EventCenter(){
    //订阅模式
      this.guanjia={
        'qishen':[],
        'baishou':[]
      }
    }
    // 在原型对象添加方法,添加观察者,
    EventCenter.prototype.$on = function(eventName,listenter){
      if(this.guanjia[eventName]){  //如果添加观察者有订阅者分组key
        this.guanjia[eventName].push(listenter)
      }else{  //如果没有的话创建一个分组添加进去
        this.guanjia[eventName]=[listenter]
      }
    }
    // 在原型对象添加方法,发布事件通知相关观察者去执行
    EventCenter.prototype.$emit = function(eventName){
      if(this.guanjia[eventName]){
        this.guanjia[eventName].forEach(element => element())
      }
    }
	// 观察者1
    function xiaoyu(){
      console.log('磊哥');
    }
	// 观察者2
    function xiaowang(){
      console.log('王哥');
    }
	// new一个实例,访问EventCenter构造函数方法
    var app = new EventCenter()
    // 调用方法添加观察者
    app.$on('qishen',xiaoyu)
    // 调用方法添加观察者
    app.$on('baishou',xiaowang)
	// 执行一个分组,他数组所有观察者都执行
    app.$emit('qishen')

从数据到视图—响应式原理

    // 观察者与发布者
    function EventCenter(){
      this.guanjia={
        'qishen':[],
        'baishou':[]
      }
    }
    // 添加观察者
    EventCenter.prototype.$on = function(eventName,listenter){
      if(this.guanjia[eventName]){
        this.guanjia[eventName].push(listenter)
      }else{
        this.guanjia[eventName]=[listenter]
      }
    }
    // 发布事件通知相关观察者去执行
    EventCenter.prototype.$emit = function(eventName){
      if(this.guanjia[eventName]){
        this.guanjia[eventName].forEach(element => element())
      }
    }
	// new一个实例
    var ec =new EventCenter()
    
    
    var data = {
      arr: 123
    }
    var vm = new MVVM({
      el:'#app',
      data
    })
    function MVVM(option){
      const {el,data} = option  
      console.log(ec);
      for(let key in data){
        Object.defineProperty(this,key,{
          set:function(newVal){
            if(newVal!==data[key]){
              data[key]=newVal
                //  触发发布者,给节点添加内容
              ec.$emit(key)
            }  
          },
          get:function(){
            return data[key]
          }
        })
    }
        //获取dom模板所有dom
    const rootDom = document.querySelector(el)
    // 把dom转为伪数组,遍历他
    Array.from(rootDom.children).forEach(node=>{
        // 判断节点有没有v-html标签
      if(node.hasAttribute('v-html')){
          // 获取标签属性
        let key = node.getAttribute('v-html')
        // 给节点添加内容
        node.innerHTML=this[key]
         // 添加监听者
        ec.$on(key,()=>node.innerHTML=this[key])
        // ec.$on(key,()=>node.innerHTML=this[key])
      }
      if(node.hasAttribute('v-model')){
        let key = node.getAttribute('v-model')
        node.value=this[key]
      }
    })
    }
    console.log(vm.arr=555);

从数据到视图,从视图到数据—双向数据绑定原理

双向绑定原理在响应式原理加上如下代码:

  ...
if(node.hasAttribute('v-model')){
        let key = node.getAttribute('v-model')
        node.value=this[key]
        ec.$on(key,()=>node.value=this[key])
      // 触发input事件给,给属性赋值
        node.addEventListener('input',e=>{
          let val = e.target.value
          this[key]=val
        })
      }
   ...

总结

  1. 响应式:当前对象值修改后,他通过视图更新

  2. 双向绑定:

    • 从数据到视图
    • 从视图到数据
  3. 谈谈对vue2响应式理解

    他是通过Object.defineProperty来对对象的属性进行拦截的,就知道在什么时间那个属性被改成那个值

    通过对视图编译,收集依赖(那个dom元素,需要在那个属性值变化时更新),把依赖关系保存到观察者模式中

    当数据被修改时,通过观察者模式发布事件,通知观察者去更新视图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

25氪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值