关于vue.js:minivue响应式312

5次阅读

共计 2673 个字符,预计需要花费 7 分钟才能阅读完成。

前言:最近关注了 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: 数据变动更新视图
正文完
 0