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部分的两个连接ui
new Vue()
data
中属性进行属性劫持
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
set
先修改msg
的值msg
依赖队列中的全部watcher
的函数,即vText
,页面的innerText
被同步更新总之几者的关系就是在observer
的get
中将对当前属性的watcher
收集进dep
,在observer
的set
中执行收集到的watcher
。this
而vue的真正的执行过程毫不是上面写的这么简单,好比watcher
的执行就毫不是简单的遍历执行,并且还对observer
进行了很大程度的简化。省略了诸如_proxy
、defineReactive
等函数。spa
写这样一个最简实现主要是为了梳理一下主干,下降阅读源码的难度。.net