前言
在捋清楚Moon中html->code和code->html的过程后,我们还需要知道一个最重要的功能:响应式数据驱动。这是MV*框架的灵魂,有了这个功能,html->code和code->html的过程才能不断地被调用,从而消灭了从前前端工程师频繁操作dom将数据同步上去的手动过程。
Observer
还记得在实例化Moon时创建的那个Observer吗?
this.$observer = new Observer(this);
顾名思义,它是个观察者,观察啥呢?当然是我们的数据咯~
观察什么数据
首先我们要知道它观察什么数据,走进Observer构造函数里看一看:
function Observer(instance) {
// Associated Moon Instance
this.instance = instance;
// Computed Property Cache
this.cache = {};
// Computed Property Setters
this.setters = {};
// Set of events to clear cache when dependencies change
this.clear = {};
// Property Currently Being Observed for Dependencies
this.target = null;
// Dependency Map
this.map = {};
}
从注释我们可以得知,它只观察computed数据和Dependency数据,computed我们知道本来就是一个计算属性,需要实时更新,那么Dependency,也就是我们常说的依赖是什么呢?让我们来一个实际的例子:
const app = new Moon({
el: "#app",
data: {
msg: "Hello Moon!",
},
computed: {
computedData: {
get() {
return this.get("msg").split("").reverse().join("")
}
}
}
})
可以观察到它在实例完成后调用了initComputed方法
initComputed
毫无疑问,这个函数负责观察computed数据
它会初始化一个setComputedProperty方法,然后遍历computed数据调用setComputedProperty。
setComputedProperty会先拿出这个事先实例化好的观察者,然后调用它的observe方法观察传入的prop,然后把prop挂载到instance.$data上,给它定义使用缓存的getter和空的setter。
Observer.prototype.observe
乍一看,有种没有写完的感觉,只是给clear属性设置了一个特定的清除缓存函数。
data中数据的响应式变更
作者没有实现$data上的数据观察,而是通过get和set来实现$data中数据的响应式变更。这就造成了使用时有种违和的感觉。从这段就可以看出来,vue中肯定是用this.msg完成获取msg的值:
computedData: {
get() {
return this.get("msg").split("").reverse().join("")
}
}
Moon.prototype.get
这个函数除了负责data,还和observe有勾结,也就是说computed也掺和了:
如果get的key不属于computed数据,就直接返回这个数据值,如果属于computed:
它会在观察者的map里说明data和computed数据的映射关系,比如:
msg: ["computedData"]
data属性为键,computed属性为值
Moon.prototype.set
当data数据发生变更的时候,只能期待set帮忙了:
可以看出它有四个步骤:
- 获取键的基础路径
- 调用自定义setter函数
- 通知观察者进行数据更新
- 将任务推入异步队列
resolvePath
它负责解析一个对象的键并设定它的值:
首先它把对象统一成了xx.yy.zz的形式,而不是xx[yyy][zz],然后把各级路径存入path里,接着取到最深路径的值赋值到obj上。
最后给obj的path[path.length-1]赋上新值val,返回path的第一个值也就是基础路径
observe.notify
观察者负责通知更新的函数:
可以看出它就做了两件事:迭代通知观察者map里的数据,并且调用clear函数清除缓存。
queueBuild
通知完了自然就要做一些事情了:
可以发现它新建了一个异步任务,里面进行了build(code->html),然后调用update钩子。期间通过$queued这个变量来控制是否新建异步任务。
总结
MV*最关键的三个特性已经分析完了,接下来我们再围绕这些核心特性衍生出的功能模块分析:指令、组件、生命周期。