- 创建vue构造函数
- 创建观察者observer
- 创建compiler
- 创建watcher
- 创建Dep
每个构造器都是有类图的,类图很清晰的说明这个构造器中含有的变量和方法
class vue
- 通过属性保存选项
把data中的成员设置getter,setter,注入到vue实例中
// 因为这里写了不是return的形式,所以需要注入到vue实例中。
data: {
msg: 'Hello Vue',
count: 100,
person: { name: 'zs' }
}
调用observer,监听数据的变化
- 调用compiler,解析插值表达式和文本节点
class observer
- 使得data中的成员拥有getter,setter方法
- 调用walk方法处理嵌套的对象,在walk方法中判断入参是对象还是其他类型(没有在外面判断),这个值得我学习。
- defineReactive方法中,在getter中收集对当前属性的依赖,在setter中触发watcher实例的update方法。
- 需要创建一个Dep的实例,开始收集依赖。
- Dep.targe && dep.addSub(Dep.target)很重要,和洋葱模型有点类似,值得学习,在watcher的constructor中会设置Dep.target的值为当前watcher对象
class compiler
- 使用childNodes获取组件的子节点(第一级的子节点),遍历,处理文本节(插值表达式属于文本节点)点和元素节点(带有指令的节点)
- 编译文本节点,使用正则表达式匹配属性,并从this.vm中拿到属性值,替换到节点中,这是节点的第一渲染,然后调用watcher实现此属性值的相应式,使得属性值在更新的时候,调用watcher构造函数中的cb函数。
- 编译元素节点,因为有格式相同的不同指令,所以我们的处理方法和处理文本节点的不同。利用名称的规律创建了update函数,调用不同指令的处理函数,textUpdater,modelUpdater。在这2个函数中更新节点的值,这是节点的第一渲染,然后调用watcher实现此属性值的相应式,使得属性值在更新的时候,调用watcher构造函数中的cb函数(这个点和文本节点的处理方式相同)。
- update方法的写法很巧妙,省去了if-else的写法,需要记住,并在平时的开发中使用哦!
- 至此,我们就实现了,使用vm.msg = 1的方法修改值后会更新到试图!但这只是这是双向中的一向哦!
- modelUpdater中的 双向绑定 实现了修改输入值更新data中数据,它是双向中的一向!
update方法中注意updateFn调用时,this的指向,this需要指向compiler函数,因为这里不能用this.updateFn调用,所以用call实现this的修正!
update (node, key, attrName) {
let updateFn = this[attrName + 'Updater']
updateFn && updateFn.call(this, node, this.vm[key], key)
}
// 处理 v-text 指令
textUpdater (node, value, key) {
node.textContent = value
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
// v-model
modelUpdater (node, value, key) {
node.value = value
new Watcher(this.vm, key, (newValue) => {
node.value = newValue
})
// 双向绑定
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
我们的判断元素类型的语句都写成了小小的函数,需要记住这种写法,并运用到平时的工作中哦!
// 判断元素属性是否是指令
isDirective (attrName) {
return attrName.startsWith('v-')
}
// 判断节点是否是文本节点
isTextNode (node) {
return node.nodeType === 3
}
// 判断节点是否是元素节点
isElementNode (node) {
return node.nodeType === 1
}
class Watcher
watcher的实现比compiler更加简单
- Dep.target = this 是为 class observer 中definReactive中的getter 中的 Dep.targe && dep.addSub(Dep.target) 作准备,因为 this.oldValue = vm[key] 会调用key属性的getter方法!接着,需要清空 Dep.target, 因为不能重复注册同一个watcher。
watcher自己需要提供一个update方法,供Dep在更新依赖的时候调用。
class Dep
它也比较短小
- 它有个subs用来保存它的所有兼听观察者watcher
- 还有个notify方法用来在自己变化的时候调用watcher者们的update函数啦
题外话:
发布订阅模式和观察者模式不同,观察者模式是发布订阅模式的高阶版本。
发布订阅模式有 事件中心 ,观察者模式没有