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

再次看了上次寫的博客關于Vue的MVVM卵迂,發(fā)現(xiàn)雖然介紹了MVVM的原理裕便,但是感覺還不夠詳細,現(xiàn)在就再次根據(jù)這篇博客寫詳細一點狭握,來看看new Vue的時候Vue究竟做了些什么事闪金。
我想,以需求作為出發(fā)點來理解原理會比較容易论颅,所以這篇博客會以提出需求 -> 解決需求的方式來寫哎垦。

項目地址,歡迎start

Vue中的MVVM原理介紹

可以先閱讀我的這篇博客了解一下關于Vue的MVVM恃疯,另外需要記住這一幅圖(很重要)漏设,這張圖就是本篇博客的概括:

image.png

需求提出

首先我們來看Vue最最基礎的用法--在id為app的元素上顯示出數(shù)據(jù)msg的內(nèi)容,平時都是Vue(Vue技術棧的話)在做這件事今妄,那么給我們自己如何實現(xiàn)呢郑口?

  • 需求


    image.png
  • 達成效果


    image.png
  • 總結(jié):
    這一步是model(數(shù)據(jù)模型) -> view(視圖)的綁定

需求分析

  • ①:首先我們要根據(jù)el獲取得到當前元素鸳碧;
  • ②:過濾出其中符合mustache語法{{...}}中的字符;
  • ③:根據(jù)②拿到的字符取到data對象中相應的數(shù)據(jù)并對對應的整個{{..}}字符進行替換犬性;

創(chuàng)建MVVM類

  • 創(chuàng)建MVVM類進行初始化
    我們給自己寫的Vue命名為MVVM瞻离,創(chuàng)建一個MVVM的類,然后獲取其中的eldata乒裆;

    image.png

    image.png

  • 但是這時候會發(fā)現(xiàn)套利,如果需要拿到data中的數(shù)據(jù)需要通過this.data.msg才能拿到,而平時用Vue時在實例中只需要this.msg就能拿到鹤耍,所以就出現(xiàn)一個問題:如何才能msg放到當前的vm實例中呢肉迫?

    image.png

  • 將data中的屬性代理到當前vm實例中
    答案是用代理的方式,這里就涉及到一個屬性Object.defineProperty(該屬性是Vue的核心稿黄,不了解的可要看一下哦)喊衫,代碼如下:

    image.png

    image.png

  • 總結(jié):這一步就是new MVVM時做的一部分初始化工作,下一步是獲取el的節(jié)點杆怕,并進行編譯工作

節(jié)點編譯器(Compiler)

  • 創(chuàng)建Compiler類
    這個類的職能是獲取data中的數(shù)據(jù)族购,并對節(jié)點中的{{...}}進行替換,所以需要傳入的參數(shù)有el和當前的vm實例财著;

    image.png

    image.png

  • 創(chuàng)建節(jié)點副本
    如果直接操控DOM元素联四,所需要的性能花銷較大撑碴,所以在Vue中采用了假節(jié)點(createDocumentFragment)撑教,通過更新假節(jié)點然后替換當前節(jié)點的方式。
    注意:在這一塊中醉拓,創(chuàng)建出來的假節(jié)點fragment對應的是el節(jié)點伟姐,所以方法是將el節(jié)點內(nèi)的所有子節(jié)點都扔進fragment中,如下代碼:

    image.png

  • 對節(jié)點副本進行編譯

    • 因為fragment跟隨的是el節(jié)點亿卤,所以需要考慮一個情況:fragment的子節(jié)點中有文本節(jié)點和元素節(jié)點兩種愤兵,這時候就需要分情況進行編譯了,這里可以通過[node.nodeType]

      image.png

    • 文本節(jié)點編譯
      文字節(jié)點的編譯又需要考慮多種情況:
      ①:{{...}}前面是否有普通文本msg{{msg}};
      ②:{{...}}后面是否有普通文本{{msg}}msg;
      ③:{{..}}里面有可能是某個對象中的值{{message.msg}}排吴;
      這時候我們把情況修改得復雜一些秆乳,包含上面所有的情況:

      image.png

      • 設想:
        如何解決問題①和問題②:創(chuàng)建一個數(shù)組,然后將截取第一個{{...}}文本之前的普通文本钻哩,作為普通文本放進數(shù)組中屹堰,然后再截取一個{{...}}文本作為tag文本放進數(shù)組中,以此類推對整個文本進行分割街氢;
        所以我們需要做的有1.創(chuàng)建一個數(shù)組用于容納分割后的文本textLIst扯键;2.創(chuàng)建一個能過濾出{{...}}文本的正則;3.分割出{{...}}文本中的鍵:比如{{msg}} 分割出 msg珊肃;5.創(chuàng)建用于定位{{...}}左后一個花括號所在index的坐標lastIndex荣刑,初始值為0馅笙;6. 事先預備一個match作為備用的正則匹配項;如下:

        image.png

      • 解決問題①:
        考慮到文本中可能有多個{{...}}文本的存在厉亏,并且還需要知道{{...}}所處的index董习,所以使用RegExp.prototype.exec就可以直接得到{{...}}里的鍵名,index的值爱只,然后賦值給match阱飘,之后套入到一個while循環(huán)中執(zhí)行,千萬不能寫成這樣,會導致死循環(huán)虱颗,原因在上面exec的mdn文檔中有解釋:

        image.png

        需要這樣寫:
        image.png

        之后有幾個{{...}}文本沥匈,while循環(huán)就會執(zhí)行幾次,得出match的值:
        image.png

        如上圖忘渔,因為已經(jīng)擁有了index高帖,所以我們現(xiàn)在可以將{{...}}前的文字拿出來放進textList中:
        image.png

        然后將{{...}}里的鍵名作為tag傳入textList中并將lastIndex置為該{{...}}文本之后:
        image.png

        這時候打印textList發(fā)現(xiàn)從最后一個{{...}}文本到文本開頭的的所有文本都已經(jīng)做好了分類,并且lastIndex也已經(jīng)為最后一個{{...}}的最后一個花括號所在的index了:
        image.png

        剩下的就是將剩余文本也作為普通文本放入textList中畦粮,因為上面的lastIndex的位置散址,所以我們直接通過lastIndex和文本的長度判斷可知最后面是否有文本需要進行compile:
        image.png

        image.png

        注意這一步不能放在while循環(huán)中做,否則會導致重復的文本放入宣赔。
        image.png

      • 我們將上面的步驟抽離出來單獨作為一個函數(shù)compileText预麸,并將textList返回出去;

        image.png

        到了這一步實際上我們已經(jīng)拿到了文本的分類片段,是{{...}}的文本則為tag儒将,否則為普通文本吏祸,下一步就是進行文本的替換了

      • 進行文本替換
        這一步中我們的主要工作是對textNode文本節(jié)點中的文本進行替換,那么需要做的步驟如下:
        1 .拿到文本節(jié)點的父節(jié)點并創(chuàng)建一個用于替換的假節(jié)點钩蚊;

        image.png

        2 .遍歷textList中進行過分類的文本片段贡翘,然后進行判斷,如果非tag文本則據(jù)此創(chuàng)建一個文本節(jié)點并放進假節(jié)點中砰逻。
        image.png

        3 .如果是tag文本則在data中進行取值鸣驱,但是這時候要考慮一個問題了,文本中的{{XXX}}實際上是一個v-text="XXX"指令蝠咆,Vue中還有很多指令踊东,例如v-model,v-for等,他們都需要在data中進行取值綁定等操作刚操,這樣的話就需要一個專門用于依據(jù)類型進行取值闸翅,綁定等工作的指令集合directives,并且當前directives里面需要一個專門處理v-text的方法赡茸、一個專門用于綁定的bind方法以及一個專門用于取值的getVMData方法缎脾,然后還需要一個專門根據(jù)指令類型用于綁定后更新視圖的集合updater,里面同樣需要一個text方法:
        image.png

        4 .之后我們在當文本類型為tag時創(chuàng)建一個空的文本節(jié)點el占卧, 然后思考text指令所承載的功能遗菠,并傳入el联喘、當前vm實例、tag文本的值辙纬,并標明類型為text:
        image.png

        image.png

        5 .解決問題③豁遭,在對tag進行綁定的過程中,免不了要先去獲取到tag文本在data中對應的值贺拣,這時候就需要考慮問題③中{{message.name}}這樣的情況了蓖谢,可以使用字符串方法split基于.分割成的數(shù)組獲取到在data中正確的值:
        image.png

  1. 現(xiàn)在我們已經(jīng)拿到了data中對應tag的值newVal,并且也有了相對應的節(jié)點譬涡,那么就執(zhí)行更新器updater中對應類型的更新方法就可以了闪幽,在這里,就是更新相對應節(jié)點的文本內(nèi)容textContent涡匀,
    image.png
  2. 最后回到compileTextNode函數(shù)中盯腌,將compile好的文本節(jié)點放進假節(jié)點中,再將textNode父節(jié)點中的文本替換即可
    image.png
  3. 這時候compile文本節(jié)點的工作就已經(jīng)做完了陨瘩,將處理后的fragment插入到真實節(jié)點el中就可以看到效果了:
    image.png

    image.png
  4. 但是此時還沒有針對普通節(jié)點進行compile腕够,所以如下html無法正常顯示,下一步就是對節(jié)點進行compile:


    image.png

    image.png

對節(jié)點進行compile(進入compileNodeElement函數(shù))

由上圖的html可以知道舌劳,針對節(jié)點進行的compile需要分為兩類:

  1. 普通節(jié)點的compile帚湘,也就是節(jié)點內(nèi)只有文本,例如<div>msg{{msg}}msg{{message.name}}8888888</div>;
  2. 帶有指令的的節(jié)點甚淡,例如:<input type="text" v-model="msg">
  • 對普通節(jié)點compile
    對普通節(jié)點進行compile很簡單大诸,因為已經(jīng)有了針對文本節(jié)點的compile,那么只需要創(chuàng)建一個通道材诽,讓普通節(jié)點進入文本節(jié)點的compile即可:


    image.png

    結(jié)果:


    image.png
  • 帶有指令的節(jié)點的compile(這里只針對v-model指令進行)
    對帶有指令的節(jié)點的節(jié)點進行compile需要做如下幾件事:

      1. 獲取節(jié)點的所有屬性名字底挫,然后遍歷,判斷是否存在指令:


        image.png
      1. 如果存在指令脸侥,就獲取該指令的值和指令類型,例如v-model=msg就獲取modelmsg
        image.png
      1. 在指令集directive和更新器updater中添加相應方法盈厘,這里是model方法睁枕,并進行處理
        image.png

        image.png
      1. 結(jié)果:


        image.png

        image.png

總結(jié)

該篇博客只對Vue中的初始化渲染原理做了介紹,也只是完成了流程圖中Compile的大部分model -> view的綁定沸手,但還未達成雙向綁定外遇,因此對數(shù)據(jù)的修改并不能對視圖進行改變,這就是下一篇博客view -> model的綁定所要介紹的:

image.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末契吉,一起剝皮案震驚了整個濱河市跳仿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捐晶,老刑警劉巖菲语,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妄辩,死亡現(xiàn)場離奇詭異,居然都是意外死亡山上,警方通過查閱死者的電腦和手機眼耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佩憾,“玉大人哮伟,你說我怎么就攤上這事⊥保” “怎么了楞黄?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抡驼。 經(jīng)常有香客問我谅辣,道長,這世上最難降的妖魔是什么婶恼? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任桑阶,我火速辦了婚禮,結(jié)果婚禮上勾邦,老公的妹妹穿的比我還像新娘蚣录。我一直安慰自己搂根,他們只是感情好薄声,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布忌警。 她就那樣靜靜地躺著址否,像睡著了一般墨辛。 火紅的嫁衣襯著肌膚如雪繁涂。 梳的紋絲不亂的頭發(fā)上淀衣,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天灯帮,我揣著相機與錄音昧港,去河邊找鬼擎椰。 笑死,一個胖子當著我的面吹牛创肥,可吹牛的內(nèi)容都是我干的达舒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叹侄,長吁一口氣:“原來是場噩夢啊……” “哼巩搏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趾代,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贯底,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撒强,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禽捆,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡笙什,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睦擂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片得湘。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖顿仇,靈堂內(nèi)的尸體忽然破棺而出淘正,到底是詐尸還是另有隱情,我是刑警寧澤臼闻,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布鸿吆,位于F島的核電站,受9級特大地震影響述呐,放射性物質(zhì)發(fā)生泄漏惩淳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一乓搬、第九天 我趴在偏房一處隱蔽的房頂上張望思犁。 院中可真熱鬧,春花似錦进肯、人聲如沸激蹲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽学辱。三九已至,卻和暖如春环形,著一層夾襖步出監(jiān)牢的瞬間策泣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工抬吟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萨咕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓拗军,卻偏偏與公主長得像任洞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子发侵,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容