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

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

项目地址,欢迎start

Vue中的MVVM原理介绍

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

image.png

需求提出

首先我们来看Vue最最基础的用法--在id为app的元素上显示出数据msg的内容,平时都是Vue(Vue技术栈的话)在做这件事,那么给我们自己如何实现呢?

  • 需求


    image.png
  • 达成效果


    image.png
  • 总结:
    这一步是model(数据模型) -> view(视图)的绑定

需求分析

  • ①:首先我们要根据el获取得到当前元素;
  • ②:过滤出其中符合mustache语法{{...}}中的字符;
  • ③:根据②拿到的字符取到data对象中相应的数据并对对应的整个{{..}}字符进行替换;

创建MVVM类

  • 创建MVVM类进行初始化
    我们给自己写的Vue命名为MVVM,创建一个MVVM的类,然后获取其中的eldata

    image.png

    image.png

  • 但是这时候会发现,如果需要拿到data中的数据需要通过this.data.msg才能拿到,而平时用Vue时在实例中只需要this.msg就能拿到,所以就出现一个问题:如何才能msg放到当前的vm实例中呢?

    image.png

  • 将data中的属性代理到当前vm实例中
    答案是用代理的方式,这里就涉及到一个属性Object.defineProperty(该属性是Vue的核心,不了解的可要看一下哦),代码如下:

    image.png

    image.png

  • 总结:这一步就是new MVVM时做的一部分初始化工作,下一步是获取el的节点,并进行编译工作

节点编译器(Compiler)

  • 创建Compiler类
    这个类的职能是获取data中的数据,并对节点中的{{...}}进行替换,所以需要传入的参数有el和当前的vm实例;

    image.png

    image.png

  • 创建节点副本
    如果直接操控DOM元素,所需要的性能花销较大,所以在Vue中采用了假节点(createDocumentFragment),通过更新假节点然后替换当前节点的方式。
    注意:在这一块中,创建出来的假节点fragment对应的是el节点,所以方法是将el节点内的所有子节点都扔进fragment中,如下代码:

    image.png

  • 对节点副本进行编译

    • 因为fragment跟随的是el节点,所以需要考虑一个情况:fragment的子节点中有文本节点和元素节点两种,这时候就需要分情况进行编译了,这里可以通过[node.nodeType]

      image.png

    • 文本节点编译
      文字节点的编译又需要考虑多种情况:
      ①:{{...}}前面是否有普通文本msg{{msg}};
      ②:{{...}}后面是否有普通文本{{msg}}msg;
      ③:{{..}}里面有可能是某个对象中的值{{message.msg}}
      这时候我们把情况修改得复杂一些,包含上面所有的情况:

      image.png

      • 设想:
        如何解决问题①和问题②:创建一个数组,然后将截取第一个{{...}}文本之前的普通文本,作为普通文本放进数组中,然后再截取一个{{...}}文本作为tag文本放进数组中,以此类推对整个文本进行分割;
        所以我们需要做的有1.创建一个数组用于容纳分割后的文本textLIst;2.创建一个能过滤出{{...}}文本的正则;3.分割出{{...}}文本中的键:比如{{msg}} 分割出 msg;5.创建用于定位{{...}}左后一个花括号所在index的坐标lastIndex,初始值为0;6. 事先预备一个match作为备用的正则匹配项;如下:

        image.png

      • 解决问题①:
        考虑到文本中可能有多个{{...}}文本的存在,并且还需要知道{{...}}所处的index,所以使用RegExp.prototype.exec就可以直接得到{{...}}里的键名,index的值,然后赋值给match,之后套入到一个while循环中执行,千万不能写成这样,会导致死循环,原因在上面exec的mdn文档中有解释:

        image.png

        需要这样写:
        image.png

        之后有几个{{...}}文本,while循环就会执行几次,得出match的值:
        image.png

        如上图,因为已经拥有了index,所以我们现在可以将{{...}}前的文字拿出来放进textList中:
        image.png

        然后将{{...}}里的键名作为tag传入textList中并将lastIndex置为该{{...}}文本之后:
        image.png

        这时候打印textList发现从最后一个{{...}}文本到文本开头的的所有文本都已经做好了分类,并且lastIndex也已经为最后一个{{...}}的最后一个花括号所在的index了:
        image.png

        剩下的就是将剩余文本也作为普通文本放入textList中,因为上面的lastIndex的位置,所以我们直接通过lastIndex和文本的长度判断可知最后面是否有文本需要进行compile:
        image.png

        image.png

        注意这一步不能放在while循环中做,否则会导致重复的文本放入。
        image.png

      • 我们将上面的步骤抽离出来单独作为一个函数compileText,并将textList返回出去;

        image.png

        到了这一步实际上我们已经拿到了文本的分类片段,是{{...}}的文本则为tag,否则为普通文本,下一步就是进行文本的替换了

      • 进行文本替换
        这一步中我们的主要工作是对textNode文本节点中的文本进行替换,那么需要做的步骤如下:
        1 .拿到文本节点的父节点并创建一个用于替换的假节点;

        image.png

        2 .遍历textList中进行过分类的文本片段,然后进行判断,如果非tag文本则据此创建一个文本节点并放进假节点中。
        image.png

        3 .如果是tag文本则在data中进行取值,但是这时候要考虑一个问题了,文本中的{{XXX}}实际上是一个v-text="XXX"指令,Vue中还有很多指令,例如v-model,v-for等,他们都需要在data中进行取值绑定等操作,这样的话就需要一个专门用于依据类型进行取值,绑定等工作的指令集合directives,并且当前directives里面需要一个专门处理v-text的方法、一个专门用于绑定的bind方法以及一个专门用于取值的getVMData方法,然后还需要一个专门根据指令类型用于绑定后更新视图的集合updater,里面同样需要一个text方法:
        image.png

        4 .之后我们在当文本类型为tag时创建一个空的文本节点el, 然后思考text指令所承载的功能,并传入el、当前vm实例、tag文本的值,并标明类型为text:
        image.png

        image.png

        5 .解决问题③,在对tag进行绑定的过程中,免不了要先去获取到tag文本在data中对应的值,这时候就需要考虑问题③中{{message.name}}这样的情况了,可以使用字符串方法split基于.分割成的数组获取到在data中正确的值:
        image.png

  1. 现在我们已经拿到了data中对应tag的值newVal,并且也有了相对应的节点,那么就执行更新器updater中对应类型的更新方法就可以了,在这里,就是更新相对应节点的文本内容textContent
    image.png
  2. 最后回到compileTextNode函数中,将compile好的文本节点放进假节点中,再将textNode父节点中的文本替换即可
    image.png
  3. 这时候compile文本节点的工作就已经做完了,将处理后的fragment插入到真实节点el中就可以看到效果了:
    image.png

    image.png
  4. 但是此时还没有针对普通节点进行compile,所以如下html无法正常显示,下一步就是对节点进行compile:


    image.png

    image.png

对节点进行compile(进入compileNodeElement函数)

由上图的html可以知道,针对节点进行的compile需要分为两类:

  1. 普通节点的compile,也就是节点内只有文本,例如<div>msg{{msg}}msg{{message.name}}8888888</div>;
  2. 带有指令的的节点,例如:<input type="text" v-model="msg">
  • 对普通节点compile
    对普通节点进行compile很简单,因为已经有了针对文本节点的compile,那么只需要创建一个通道,让普通节点进入文本节点的compile即可:


    image.png

    结果:


    image.png
  • 带有指令的节点的compile(这里只针对v-model指令进行)
    对带有指令的节点的节点进行compile需要做如下几件事:

      1. 获取节点的所有属性名字,然后遍历,判断是否存在指令:


        image.png
      1. 如果存在指令,就获取该指令的值和指令类型,例如v-model=msg就获取modelmsg
        image.png
      1. 在指令集directive和更新器updater中添加相应方法,这里是model方法,并进行处理
        image.png

        image.png
      1. 结果:


        image.png

        image.png

总结

该篇博客只对Vue中的初始化渲染原理做了介绍,也只是完成了流程图中Compile的大部分model -> view的绑定,但还未达成双向绑定,因此对数据的修改并不能对视图进行改变,这就是下一篇博客view -> model的绑定所要介绍的:

image.png

Vue中的MVVM--view -> model的绑定

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

推荐阅读更多精彩内容