Vue的主要原理中主要用到了定义的这么几个函数Dep,Watcher,observer。
咱们来使用这几个函数简单的实现一下vue构造函数数据绑定和相互依赖部分,梳理一下它们之间的关系。
省略了编译部分和proxy代理与其余的一些复杂逻辑。html
Dep是依赖类,简要实现为vue
class Dep { constructor () { // 放当时属性的观察者 this.subs = [] } } // target 用来挂载当时的watcher观察者 Dep.target = null
作属性劫持,并作点其余事情git
function observer (vm, key, val) { let dep = new Dep() Object.defineProperty(vm, key, { /** * get主要作两个事情 * 1. 收集观察当前key的wathcer(即依赖当前key的操做) * 2. 获取值 */ get () { // 这是做用1 if (Dep.target) { dep.subs.push(Dep.target) } // 这是做用2 return val }, /** * set也是两个事情 * 1. 修改目标值 * 2. 执行依赖当前key的watcher */ set (newVal) { // 这是做用1 val = newVal // 这是做用2 for(cb of dep.subs) { cb.call(vm) } } }) }
Watcher是观察者类,用来建立依赖某属性的操做(如指令,渲染,计算属性等)github
class Watcher { /** * vm: 实例 * cb: 依赖某属性的操做函数 */ constructor (vm, cb) { // 把当前的操做挂载到Dep上 Dep.target = cb /** * 执行操做,两个做用 * 1. 进行操做的初始化 * 2. 触发属性的get方法,使当前cb被收集 */ cb.call(vm) Dep.target = null } }
那么咱们就使用上面定义好的函数写个例子闭包
<div> <p class="text"></p> <div>
let vm = new Vue({ // 假设有data data: {msg: 1}, // 有某个v-text操做,咱们抽象为vText函数,依赖属性msg(表明全部依赖其余属性的操做) renderFun: { vText () { document.querySelector('.text').innerText = this.msg } } }) // 修改vue实例的值,观察变化 vm.msg = 333
那么咱们也写一个vue的简易构造函数函数
class Vue { constructor (options) { let data = options.data let renderFun = options.renderFun // initData Object.keys(data).forEach(key => { observer(this, key, data[key]) }) // 模拟计算属性,watcher,指令等依赖属性的操做 Object.keys(renderFun).forEach(key => { new Watcher(this, renderFun[key]) }) } }
完整的代码能够看demo部分的两个连接this
new Vue()
对data进行初始化,对data
中属性进行属性劫持.net
get
进行观察者watcher
的收集和值得获取;set
进行值的更新和依赖队列中watcher
的执行computed\watcher
或模板编译
过程当中的指令
函数进行初始化,咱们以renderFun
代替renderFun
中的每一个功能函数进行new Watcher()
工做以vText
为例子,在new Wathcer()
过程当中代理
vText
挂载到全局通用的Dep.target
上vText
,其中有读vm.msg
的操做,则触发msg属性的get,进入Dep.target
判断,将Dep.target
即vText
收集进msg
的subs
依赖队列中,此时vText
执行完毕,页面innetText
被修改Dep.target
置空执行vm.msg = 333
,则触发msg
的set
code
set
先修改msg
的值msg
依赖队列中的全部watcher
的函数,即vText
,页面的innerText
被同步更新总之几者的关系就是在observer
的get
中将对当前属性的watcher
收集进dep
,在observer
的set
中执行收集到的watcher
。
而vue的真正的执行过程毫不是上面写的这么简单,好比watcher的执行就毫不是简单的遍历执行,并且还对observer进行了很大程度的简化。咱们还省略了诸如_proxy
、defineReactive
等出现频率较高的函数。写这样一个最简实现主要是为了梳理一下主干,下降阅读源码的难度。