Vue中的MVVM(2)--view -> model的绑定

再次看了上次写的博客关于Vue的MVVM,发现虽然介绍了MVVM的原理,但是感觉还不够详细,现在就再次根据这篇博客写详细一点,来看看new Vue的时候Vue究竟做了些什么事。
我想,以需求作为出发点来理解原理会比较容易,所以这篇博客会以提出需求 -> 解决需求的方式来写。

项目地址,欢迎start

Vue中的MVVM原理介绍

可以先阅读我的这篇博客了解一下关于Vue的MVVM,另外需要记住这一幅图(很重要),这张图就是本篇博客的概括:

image.png

回顾

继上一篇文章Vue中的MVVM--model -> view的绑定,我们完成了对页面的初始化渲染,达成了如下要求:

image.png

image.png

但还未完成view -> model的绑定,所以不能通过修改数据来触发视图的更新,今天就来完成剩余部分

提出需求

继上图:


image.png

我们需要做到的是:当修改输入框的数据时,上面的文字也随之进行刷新。

分析

先来看看还未完成的部分:

image.png

其中包含观察者Observer,监听器Watcher,然后还有一个Dep,过程是:

  1. Compiler中监听数据的变化并绑定监听器;
  2. 在观察者Observer中实现对所有数据的gettersetter
  3. 监听器Watcher把更新事件添加进Dep的事件队列中;
  4. 观察者Observer发现数据产生变化的时候通知Dep
  5. Dep把事件队列中的更新事件全部执行一遍;

总结下来就是实现两个事情:

  1. 添加数据依赖;
  2. 触发数据变更事件;
    接下来就先创建WatcherObserverDep三个类;
    image.png

    image.png

    image.png

先来看看Dep是什么

根据上面的的步骤描述,很容易感觉到Dep像是一个容器,存储着对视图的更新事件,是的,这是一个发布订阅模式的实现,该模式包含事件队列subs,添加事件方法addSub,执行事件队列函数notify,移除事件队列里的事件removeSub

image.png

看完发布订阅模式后,继续我们的流程。

添加依赖

  • 在Compiler中监听数据的变化并绑定监听器
    在上一篇对model -> view的绑定中我们有一个针对数据和指令统一进行绑定的方法bind,为了不和后面的v-bind指令冲突,现在改为了bindData;

    image.png

    在这个函数承载的功能有获取tag文本和执行视图的更新,所以我们可以在这个函数中添加对数据的监听器Watcher,因为后面需要把更新视图的事件添加进Dep中,所以Watcher中需要的参数要有一个更新事件也就是更新器updater中的视图更新函数,此外将当前vm实例和得到的data键值也传进去备用;
    compile中添加数据监听器

    更新器

    watcher

    接着

  • 在观察者Observer中实现对所有数据的getter和setter
    注意这一步需要考虑到数据中含有嵌套的对象,需要进行递归操作才能全部添加gettersetter,使用的是Object.definedPropertyObserver接受的参数是data对象:

    image.png

    然后在MVVM类中代入data并执行observer:
    image.png

    接着对所有data中的属性绑定gettersetter,这一步需要进行递归操作:
    image.png

    最后回到Compiler中,实现视图对数据的修改:
    比如在输入框中修改数据直接反应到data
    image.png

    image.png

    来看看成果:
    image.png

    这个时候在视图上对数据进行的修改就可以反映到data上,并触发该数据的setter函数;

  • 监听器Watcher把更新事件添加进Dep的事件队列中;
    这一步需要考虑一个问题:在什么时候怎么样把更新事件添加到Dep中去?
    回顾上面所写的,data中的每一个属性都有一个对应的Watcher,可以在Watcher中获取得到对应的data中的属性。那么在这个获取的过程中,又会触发该属性的getter,就可以考虑在该属性的getter中添加,分解成一下步骤就是:
    ① 把这个Watcher通过构造函数本身的属性target保留在Dep中,然后去data中取值;

    image.png

    ② 取值的时候触发Observer中该属性的getter,在Observer中new一个Dep实例出来,判断如果Dep类的target非空(也就是该属性已被有监听器),则触发依赖添加事件depend;
    image.png

    ③ 这时候的Dep.target就是被监听属性的Watcher,在Dep类中添加一个方法depend,用来把该属性的Watcher添加进事件队列subs中,但是这一步要当前的Watcher,需要在Watcher类中进行触发,所以在Watcher中创建一个函数addDep,把Dep的实例作为参数放进去,然后在addDep中进行更新事件的添加:
    image.png

    image.png

    然后置空Dep.target,用于下一个数据的依赖添加
    image.png

    现在我们来看看subs中有些什么
    image.png

    可见msg被引用了两次就被监听了两次,这时候只要当msg这个数据发生变化并触发setter时,将subs中所有的watcher实例里的更新回调update拉出来执行即可

  • 更新视图

  1. 更新视图的时候,我们先要获取当前的数据新值,然后作为参数放进回调函数中,并且还要对新的数据进行上面的依赖添加步骤,那么Watcher还需要一个update函数用来统一做这个事:
    image.png
  2. Observersetter中触发Depnotify方法,进行视图的更新:
    image.png
  3. 到了这步其实就已经达成效果了:


    image.png
  • 修复bug
    虽然MVVM双向绑定的功能已经达成,但是还是有不少bug的,其中最严重的有两个
  1. 当我们多次更新数据的时候,会发现添加进subswatcher发生了递增的现象,所以当快速更新数据时就会导致执行函数过多而页面崩溃;

    image.png

    造成这个现象的原因是在进行第一次的更新时,watcher将同一个数据的新值也进行了依赖添加,也就是let newVal = this.get()这一段;
    image.png

    既然知道了原因,那么解决起来也很简单,给每一个被监听的对象都添加一个id即可。
    因为添加sub的操作是在Watcher中进行的,所以在Watcher中创建一个对象depIds
    image.png

    然后给每一个Dep都添加一个不同的id
    image.png

    最后在Watcher中判断depIds是否已经有这个id的Dep实例存在,如果没有则添加进去并执行addSub,否则不执行:
    image.png

    效果,无论怎么修改,都只会有固定数量的Watcher存在:
    image.png

  2. 当修改数据为对象的时候,这个对象没有进行监听,这个也好解决,只要在setter中进行判断即可,若为对象则针对该对象重新进行监听

    image.png

总结

到这里为止,我们就完成了view -< model的绑定,并且知道在new Vue的时候大致做了一些什么事了,剩下的就是逐步完善,例如对更多指令的支持,对methods以及computedwatch的支持。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容