美文网首页moonjs
深入浅出MV*框架源码(四):Moon的数据响应式变更实现

深入浅出MV*框架源码(四):Moon的数据响应式变更实现

作者: 云峰yf | 来源:发表于2017-12-19 21:45 被阅读0次

前言

在捋清楚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数据


initComputed.jpg

它会初始化一个setComputedProperty方法,然后遍历computed数据调用setComputedProperty。
setComputedProperty会先拿出这个事先实例化好的观察者,然后调用它的observe方法观察传入的prop,然后把prop挂载到instance.$data上,给它定义使用缓存的getter和空的setter。

Observer.prototype.observe

observe.jpg

乍一看,有种没有写完的感觉,只是给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.jpg

如果get的key不属于computed数据,就直接返回这个数据值,如果属于computed:


get-computed.jpg
它会在观察者的map里说明data和computed数据的映射关系,比如:
msg: ["computedData"]

data属性为键,computed属性为值

Moon.prototype.set

当data数据发生变更的时候,只能期待set帮忙了:


set.jpg

可以看出它有四个步骤:

  1. 获取键的基础路径
  2. 调用自定义setter函数
  3. 通知观察者进行数据更新
  4. 将任务推入异步队列
resolvePath

它负责解析一个对象的键并设定它的值:


resolveKeyPath.jpg

首先它把对象统一成了xx.yy.zz的形式,而不是xx[yyy][zz],然后把各级路径存入path里,接着取到最深路径的值赋值到obj上。
最后给obj的path[path.length-1]赋上新值val,返回path的第一个值也就是基础路径

observe.notify

观察者负责通知更新的函数:


notify.jpg

可以看出它就做了两件事:迭代通知观察者map里的数据,并且调用clear函数清除缓存。

queueBuild

通知完了自然就要做一些事情了:


queueBuild.jpg

可以发现它新建了一个异步任务,里面进行了build(code->html),然后调用update钩子。期间通过$queued这个变量来控制是否新建异步任务。

总结

MV*最关键的三个特性已经分析完了,接下来我们再围绕这些核心特性衍生出的功能模块分析:指令、组件、生命周期。

相关文章

网友评论

    本文标题:深入浅出MV*框架源码(四):Moon的数据响应式变更实现

    本文链接:https://www.haomeiwen.com/subject/dukoixtx.html