一.Vue响应式原理
首先要了解几个概念:
数据响应式:数据模型仅仅是普通的Javascript对象,而我们修改数据时,视图会进行更新,避免了繁琐的DOM操作,提高开发效率。
双向绑定:数据改变,视图改变,数据也随之改变,我们可以使用v-model在表单上创建双向数据绑定。
数据驱动是Vue最独特的特性之一:开发过程中仅需要关注数据本身,不需要关心数据是如何渲染到视图。
vue2.X中的响应式原理是基于defineProperty,兼容IE8以上版本,核心原理代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   |       let data={  msg:'hello',  count:10   } let vm={} proxyData(data) function proxyData(data){     Object.keys(data).forEach(key=>{         Object.defineProperty(vm,key,{             enumerable:true,             configurable:true,             writeable:true,             //获取值的时候执行             get(){                 console.log('get:',key,data[key])                 return data[key]             },             //设置值的时候执行             set(newValue){                 data[key]=newValue                 console.log('set:',key,newValue)                 document.querySelector('#app').textContent=data[key]             }         })     }) }  vm.msg //获取(get方法) hello  vm.msg='hello World' //设置新属性值并渲染到页面(set方法)  vm.msg //hello World  
 
  | 
 

vue3.X中的响应式原理是基于Proxy,直接监听对象,而非属性,ES6中新增,IE不支持,性能由浏览器优化,性能比defineProperty要好,代码的话相比较defineProperty要简洁一些,对于多个属性的值不需要进行循环遍历处理。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   |         let data={     msg:'hello',     count:0 }
  //模拟 Vue 实例 let vm=new Proxy(data,{     //执行代理行为的函数     //当访问 vm 的成员会执行     get(target,key){         console.log('get,key:',key,target[key])         return target[key]     },     set(target,key,newValue){         console.log('set,key:',key,newValue)         if(target[key] === newValue){             return          }         target[key]=newValue         document.querySelector("#app").textContent=target[key]     } }) //测试 vm.msg='Hello World' console.log(vm.msg)
 
  | 
 

二.发布订阅模式和观察者模式
1.发布/订阅模式
这个概念有些抽象,下面举个例子说明下,家长比较关心孩子成绩,天天问孩子成绩出来没,假设可以到孩子所在班级去订阅孩子成绩,一旦考试成绩出来,相当于触发了一个事件,最后有班级的老师以短信的形式通知给家长,
不需要天天问孩子成绩出来没,家长就是事件的订阅者,老师是事件的发布者,孩子所在的班级可以假想成一个事件的中心。vue中的自定义事件都是基于发布/订阅模式的。下面模拟下发布订阅模式的运行机制:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | //事件触发器         class EventEmitter(){             constructor(){                 // 初始化对象{ 'click':[fn1,fn2],'change':[fn] }                 this.subs=Object.create(null)             }             //注册事件             $on(eventType,handler){                 this.subs[eventType] = this.subs[eventType] || []                 this.subs[eventType].push(handler)             }             //触发事件             $emit(eventType){                 if(this.subs[eventType]){                     this.subs[eventType].forEach(handler => {                         handler()                     })                 }             }         }         //测试                  let em =new EventEmitter()                  em.$on('click',()=>{             console.log('click1')         })         em.$on('click',()=>{             console.log('click2')         })         em.$emit('click') //打印结果 click1,click2
 
  | 
 

二.观察者模式
观察者模式和订阅模式的区别是没有事件中心,只有发布者和订阅者,并且发布者需要知道订阅者的存在.
概念:
观察者 –Watcher
update():当事件发生时,具体要做的事情。
发布者 –Dep
subs数组:存储所有的观察者
addSub():添加观察者
notify():当事件发生,调用所有观察者的update()方法

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | //发布者-目标         class Dep{             constructor() {                 //记录所有的订阅者                 this.subs=[]             }             //添加订阅者             addsub(sub){                 if(sub && sub.update){                     this.subs.push(sub)                 }             }             //发布通知             notify(){                 this.subs.forEach(sub=>{                     sub.update()                 })             }         }         //订阅者-观察者         class Watcher{             update(){                 console.log('update')             }         }         //测试         let dep=new Dep()         let watcher=new Watcher()         dep.addsub(watcher)         dep.notify() //打印结果 update
 
  | 
 

总结:
观察者模式是由具体目标调度,比如当事件触发,Dep就会去调用观察者的方法,所以观察者的订阅者和发布者之间是存在依赖的。
发布订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。
