前言:最近关注了vue,想比照一下和react的实现的区别,不便当前我的项目做选型。
打算理解比照一下根本实现,能力写出更合乎 框架设计思维高效率的代码,出了问题能力更好的排错。
vue响应式模仿
筹备工作
- 数据驱动
- 响应式外围
- 公布订阅和观察者
数据驱动
- 数据响应式:数据模型仅仅是一般的js对象,而当咱们批改数据时,视图会进行对应的更新,防止了繁琐的dom操作,进步开发效率
- 双向绑定:数据扭转,视图扭转;视图扭转,数据也随之扭转,应用v-model在表单元素上创立双向数据绑定
- 数据驱动是vue最独特个性之一:开发过程仅须要关注数据自身,不须要关怀数据是如何渲染到视图的
数据响应式
vue2是用的Object.defineProperty来进行对对象的get,set进行劫持来实现的双向绑定相似:
let data ={msg:'hello'}let vm={}Object.defineProperty(vm,'msg',{ enumerable:true, configurable:true, get(){ return data.msg } set(newValue){ if(newValue === data.msg){ return } data.msg=newValue document.querySelector('#app').textContent=data.msg }})vm.msg='HelloWorld'console.log(vm.msg)
vue3应用的是代理对象,间接监听对象,不监听属性,性能由浏览器优化,
let data={msg:'hello',count:0}let vm = new Proxy(data,{ get(target,key){ return target[key] } set(target,key,newValue){ if(target[key] === newValue){ return } target[key] = newValue document.querySelector('#app').textContent=target[key] }})
贴个图来展现下俩的区别
Object.defineProperty
只能劫持对象的属性,因而咱们须要对每个对象的每个属性进行遍历。Object.defineProperty
不能监听数组。是通过重写数据的那7个能够扭转数据的办法来对数组进行监听的。Object.defineProperty
也不能对es6
新产生的Map
,Set
这些数据结构做出监听。
Proxy不会间接侵入对象去做劫持,而是间接对对象整体进行包装一层。
不过咱们目前实现的简易版vue是基于vue2去做的。
公布/订阅模式和观察者模式
公布/订阅模式
- 订阅者
- 发布者
- 信号核心
阐明:咱们假设,存在一个"信号核心",某个工作执行实现,就向信号核心"公布"(publish)一个信号,其余工作能够向信号核心"订阅"(subscribe)这个信号,从而晓得什么时候本人能够开始执行。这就叫做"公布/订阅模式"(publish-subscribe pattern)
vue中的自定义事件就是一个公布订阅模式$emit,$on.
//eventBus.js//事件核心let eventHub=newVue()//ComponentA.vue//发布者addTodo:function(){//公布音讯(事件) eventHub.$emit('add-todo',{text:this.newTodoText}) this.newTodoText='' }//ComponentB.vue//订阅者created:function(){//订阅音讯(事件) eventHub.$on('add-todo',this.addTodo)}
咱们模仿一个vue的公布订阅,如下
class EventEmitter{ constructor(){ this.subs={} } $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() }) } }}var bus= new EventEmitter()//注册事件bus.$on('click',function(){ console.log('click')})bus.$on('click',function(){ console.log('click1')})//触发事件bus.$emit('click')
观察者模式
- 观察者watcher:update当事件产生时更新
指标(发布者)-Dep
- subs数组:贮存所有观察者
- addsub增加观察者
- notify事件产生时告诉观察者
- 没有事件核心
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('更新六') }}let dep= new Dep()let watcher = new Watcher()dep.addSub(watcher)dep.notify()
总结
观察者模式是由具体指标调度,比方当事件触发,Dep 就会去调用观察者的办法,所以观察者模式的订阅者与发布者之间是存在依赖的。
公布/订阅模式由对立调度核心调用,因而发布者和订阅者不须要晓得对方的存在。
从应用层面上讲:
- 观察者模式,多用于单个利用外部
- 公布订阅模式,则更多的是一种跨利用的模式(cross-application pattern),比方咱们罕用的消息中间件
而后咱们剖析一下vue中的实例化及更新流程,如图
- vue:把传入的data注入vue实例,并且转换为set/get
- Observer:可能对数据对象的所有属性进行监听,如有变动可拿到最新值并告诉 Dep,Dep再调用观察者更新
- Compiler:解析每个元素中的指令/插值表达式,并替换成相应的数据
- Dep:增加观察者(watcher),当数据变动告诉所有观察者
- Watcher:数据变动更新视图