[學習vue]源碼解析01

今天聽了一個大牛講課 除了膜拜 還是膜拜?

深入理解Vue的底層原理 通過一個手寫的過程可以深入的理解一下vue的底層設計原理旱眯,首先vue工作機制是怎么樣的 其次的vue的響應機制是怎么樣的贺纲,在vue的響應中依賴收集與追蹤是怎么實現(xiàn)的呢,最后是怎么編譯compile呢补憾?我們帶著這些問題去深入探究一下

vue工作機制

初始化,在new vue()之后,首先在內(nèi)部執(zhí)行了一個初始化方法涌乳,它做的就是一些最基礎的東西的初始化站欺,比如說初始化生命周期姨夹,我們知道有很多生命周期的鉤子,還有一些props矾策,還有我們一些數(shù)據(jù)data的響應化等磷账,其中最重要的是通過object.defineProperty設置getter和setter函數(shù),用來實現(xiàn)響應式以及依賴收集贾虽。?

在初始化之后調(diào)用$mount來執(zhí)行掛載函數(shù)逃糟,我們知道Vue的初始化就是通過$mount來實現(xiàn)的,$mount其實就是要指定一個掛載節(jié)點蓬豁,可能會是一個目標節(jié)點绰咽,也有可能會是一個dom節(jié)點,最終就是告訴我們vue將把那些寫好的模板通過編譯以后達到更新以后這個最新的東西 我到底要顯示在什么地方地粪,就是$mount最終指定的那個目標取募,然后$mount會啟動這個編譯器compile,這個編譯器最重要的事情就是對我們的Template里的東西進行一遍掃描蟆技,做parse optimize generate這三件事玩敏,compile在這個階段會生成一些渲染函數(shù)或者也可以叫更新函數(shù)斗忌,會生成一顆樹,我們叫虛擬節(jié)點樹旺聚,將來在做數(shù)據(jù)更新的時候飞蹂,其實我們改變的數(shù)據(jù)并不是真正的dom操作,而是這個虛擬dom上的數(shù)值翻屈,當我們準備更新之前我們會做一個diff算法的比較陈哑,通過最新的值和之前的老值進行比較,從而計算出我們應該做的最小的dom更新伸眶,然后我們才開始執(zhí)行到這個patch步驟來打補丁做界面更新惊窖,這樣兒做的目的是用js里面的計算時間來換dom操作時間,我們知道瀏覽器的瓶頸在對頁面操作這一塊兒比較耗時間厘贼,Vue的核心在于減少頁面渲染的次數(shù)和數(shù)量界酒,compile除了編譯渲染函數(shù)之外,還會做一個依賴收集的工作嘴秸,通過這個依賴收集我們可以知道當頁面數(shù)據(jù)發(fā)生變化的時候我應該去更新頁面中的那一個dom節(jié)點毁欣,這也就是將來這個數(shù)據(jù)發(fā)生變化的時候,我們可以通過這個watcher觀察者來知道數(shù)據(jù)發(fā)生變化岳掐,這時候調(diào)用更新渲染函數(shù)來打補丁凭疮。

new vue

上述提到的編譯器在掃描dom的時候做的三件事兒parse optimize generate

1 parse是使用正則解析template中的vue指令變量等,形成語法樹AST

2 optimize 標記一些靜態(tài)節(jié)點串述,用作后面的性能優(yōu)化执解,在diff的時候直接略過

3 generate 把第一部生成的AST轉(zhuǎn)化為渲染函數(shù)


我們今天要實現(xiàn)一個自己的mvvm框架,實現(xiàn)一個observer數(shù)據(jù)劫持監(jiān)聽纲酗,當數(shù)據(jù)發(fā)生變化的時候衰腌,通知watcher變化,讓他去調(diào)視圖更新,從而去做界面的更新觅赊,框架開始也會做一些編譯的過程右蕊,會初始化視圖,在初始化視圖的同時還做了另外一件事情吮螺,就是初始了我們的觀察者watcher

簡化版

更新視圖

數(shù)據(jù)修改解發(fā)setter饶囚,然后監(jiān)聽器會通知進行修改,通過對比兩個dom樹规脸,得到改變的地方坯约,就是patch然后需要把這些差異修改即可

下面來一波實戰(zhàn)

vue響應式的原理:defineProperty

首先我定義一個對象obj 我期望obj.name='xx' 這個操作可以直接顯示在標簽內(nèi),這種操作是不是就是所謂的數(shù)據(jù)驅(qū)動莫鸭,數(shù)據(jù)的響應式闹丐,我們平常在寫的Vue的時候是不是就是這樣的,當一個屬性發(fā)生變化的時候被因,界面中的值就會動態(tài)的發(fā)生變化卿拴。我們使用object.defineProperty來添加屬性

數(shù)據(jù)響應原理

通過defineProperty我們就可以知道vue數(shù)據(jù)響應式原理衫仑,來給我們的Data添加屬性 當這個屬性發(fā)生改變的時候,我們就可以指定的規(guī)則來作更新堕花。

那么我們在面試的時候怎么回答 vue的原理是怎樣的呢?

從原理上來講vue是利用了Object的defineProperty的屬性文狱,它把我們數(shù)據(jù)data中放的每一個屬性,都定義成一個屬性缘挽,賦予了getter和setter,這樣兒的話讓我們有機會去監(jiān)聽這些屬性的變化瞄崇,當這些屬性發(fā)生變化的時候,我們可以通知那些需要更新的地方去更新壕曼。

我們先簡單模擬一個vue數(shù)據(jù)更新的類來實現(xiàn)數(shù)據(jù)響應:

KVue.js
index.html

我們在defineProperty中的set中監(jiān)聽到了數(shù)據(jù)更新苏研。通過運行index.html我們可以看到test 和 bar的更新都打印出來了。

當發(fā)現(xiàn)在數(shù)據(jù)發(fā)生的變化的時候腮郊,我們需要做界面的更新摹蘑,上述中我們只是打印出的變化,因此引出來數(shù)據(jù)的依賴收集的概念

比如在界面中我們引用了name1 name2 name1 這時候我們created name1和name3的時候轧飞,會發(fā)生什么事情呢衅鹿?

name1我會發(fā)現(xiàn)在頁面中有兩個依賴,這樣兒在name1變化的時候过咬,我通知兩個部分變化就可以了大渤,這時候name3發(fā)生變化,其實不會做任何通知援奢,因為跟本沒有任何依賴兼犯,這就是為什么在程序開始之前一定會對模板進行一次遍歷,然后我們會從中找出一些和我數(shù)據(jù)有依賴的部分集漾,收集保存下來,在數(shù)據(jù)需要更新的時候調(diào)用砸脊。

依賴收集需要引入兩個概念具篇,一個是依賴對象depnice 一個是監(jiān)聽對象watcher,這兩個對象首先遵從一個發(fā)布訂閱模式,dep是訂閱者凌埂,它非常關心我們的數(shù)據(jù)發(fā)生變化驱显,觀察者其實是我們例子中的setter函數(shù),當數(shù)據(jù)發(fā)生變化的時候瞳抓,dep發(fā)出通知埃疫,去調(diào)用所有的watcher。

我們定義了一個dep類的對象孩哑,用來收集watcher對象栓霜,讀數(shù)據(jù)的時候,會觸發(fā)getter函數(shù)把當前的watcher對象(存放在Dep.target中)收集到Dep類中横蜒,寫數(shù)據(jù)的時候胳蛮,則會觸發(fā)stter方法销凑,通知Dep類調(diào)用notyfy來觸發(fā)所有的watcher對象的update方法更新對應視圖。

這里一定要注意仅炊,每一個依賴針對于一個單個的屬性斗幼,每個依賴當中還有可能會有多個watcher,key出現(xiàn)幾次就會有幾個watcher抚垄。

編譯compile

核心邏輯獲取dom,遍歷dom,獲取{{}}格式的變量蜕窿,以及每個dom的屬性,截獲k- @等開頭設置響應式

在compile.js中呆馁,我們首先需要將內(nèi)容轉(zhuǎn)換為代碼片斷桐经,以減少對dom的操作,然后進行編譯智哀,將編譯結果再追加到宿主對象el上次询。

在上述步驟中 轉(zhuǎn)換代碼片斷的方法node2Fragment,我們先創(chuàng)建一個代碼片斷瓷叫,通過查找宿主對象el上的firstChild屯吊,逐次的添加到新創(chuàng)建的代碼片斷中,最后返回這個代碼片斷的集合摹菠。

在編譯的方法compile中盒卸,遍歷代碼片斷,判斷是元素對象還是插值對象次氨,對應做的相應的操作蔽介,這個時候由于遍歷對象中也可能會包含子節(jié)點,所以我們要通過判斷el.childNodes節(jié)點是否存在來做遞歸判斷煮寡。

這個時候當我們在kvue.js中去new Compile(options.el, this)一個compile實例的時候虹蓄,就可以完成字符串轉(zhuǎn)換了,但是在我們的生命周期鉤子函數(shù)created中 如果有一個setTimeout來this.name的時候幸撕,就不會發(fā)生任何變化了薇组,這是因為我們還沒有做任何依賴收集的工作,當屬性更新的時候setter函數(shù)沒有被觸發(fā)坐儿,所以我們需要一個更新函數(shù)update 添加依賴收集律胀。

這時我們就需要改一下compileText方法,添加一個update方法的通用方法貌矿,執(zhí)行第一次修改炭菌,然后添加依賴。

首先watcher的構造函數(shù)需要接收三個參數(shù)vm,key,cb逛漫,這時候我們回到kvue.js 我們需要在Watcher函數(shù)中黑低,需要對三個參數(shù)做引用,接下來我們添加依賴屬性的地方尽楔,可以再讀一次添加的屬性投储,由次來添加依賴第练,觸發(fā)setter,然后再置空玛荞。避免重復添加娇掏,下一次再創(chuàng)建的時候還是這個過程。這時候update函數(shù)里勋眯,就可以直接執(zhí)行cb

到此這個流程基本上就串起來了婴梧,這時候我們會發(fā)現(xiàn)在watcher中,觸發(fā)屬性的時候使用的是vm客蹋,而不是$data塞蹭,所以要變成大家熟悉的那種寫法,因此需要寫一個代理讶坯。

接下來的代理工作番电,我們要回到observe中,我們在定義defineReactive的時候辆琅,我們還可以再定義一個代理proxyData漱办,代理data中的屬性到vue的實例上,這時候我們就可以用vue.xx 來直接使用了婉烟。

proxyData

我們把新傳進來的值賦值給$data, this.$data[key]的重新賦值又會觸發(fā)上面我們定義在dfeinReactive中的data的setter方法娩井,然后又開始通知,這樣兒就串起來了似袁。

當別人問你vue的編譯過程是怎么樣的時候洞辣,你怎樣回答?

我們先說什么是編譯昙衅,為什么要編譯扬霜,首先因為vue寫的html模板,是瀏覽器識別不了的而涉,我們通過編譯的過程畜挥,可以進行依賴收集,進行依賴收集之后我們就把Data中的數(shù)據(jù)模型和視圖之間產(chǎn)生依賴關系婴谱,當模型發(fā)生變化的時候,我們就可以通知這些依賴的地方讓他們進行更新躯泰,這就是我們執(zhí)行編譯的目的谭羔,我們把這些界面全部編譯以后,更新操作麦向,我們就可以做到模型驅(qū)動視圖的變化這就是編譯的過程瘟裸。

雙向綁定的原理是什么?

我們在做雙向綁定的時候 通常會使用一個v-model這樣的指令放在input這樣的一個輸入元素上诵竭,我們在編譯的時候可以解析這個v-model 话告,我在做操作的時候有兩件事情兼搏,第一件事情我把當前vmodel所屬的這個元素上加了一個事件監(jiān)聽,這樣如果input會生變化的時候沙郭,我就可以把最新的值設置到vue的實例上佛呻,因為vue的實例已經(jīng)實現(xiàn)了數(shù)據(jù)的響應化,它的響應化的setter函數(shù)病线,會觸發(fā)頁面中所有依賴的更新吓著,所以跟這個數(shù)據(jù)相關的所有部分都更新了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末送挑,一起剝皮案震驚了整個濱河市绑莺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惕耕,老刑警劉巖纺裁,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異司澎,居然都是意外死亡欺缘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門惭缰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浪南,“玉大人,你說我怎么就攤上這事漱受÷缭洌” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵昂羡,是天一觀的道長絮记。 經(jīng)常有香客問我,道長虐先,這世上最難降的妖魔是什么怨愤? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蛹批,結果婚禮上撰洗,老公的妹妹穿的比我還像新娘。我一直安慰自己腐芍,他們只是感情好差导,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猪勇,像睡著了一般设褐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天助析,我揣著相機與錄音犀被,去河邊找鬼。 笑死外冀,一個胖子當著我的面吹牛寡键,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锥惋,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼昌腰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膀跌?” 一聲冷哼從身側(cè)響起遭商,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捅伤,沒想到半個月后劫流,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡丛忆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年祠汇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熄诡。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡可很,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凰浮,到底是詐尸還是另有隱情我抠,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布袜茧,位于F島的核電站菜拓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笛厦。R本人自食惡果不足惜纳鼎,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裳凸。 院中可真熱鬧贱鄙,春花似錦、人聲如沸姨谷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菠秒。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間践叠,已是汗流浹背言缤。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留禁灼,地道東北人管挟。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像弄捕,于是被迫代替她去往敵國和親僻孝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 最近利用空閑時間又翻看了一遍Vue的源碼守谓,只不過這次不同的是看了Flow版本的源碼穿铆。說來慚愧,最早看的第一遍時對F...
    海洋之木閱讀 60,588評論 15 175
  • 大家都知道斋荞,閱讀源碼可以幫助自己成長荞雏。源碼解析的文章也看了不少,但是好記性不如爛筆頭平酿,看過的東西過段時間就忘的差不...
    snow_in閱讀 4,736評論 0 7
  • 前言 Vue.js 的核心包括一套“響應式系統(tǒng)”凤优。 “響應式”,是指當數(shù)據(jù)改變后蜈彼,Vue 會通知到使用該數(shù)據(jù)的代碼...
    NARUTO_86閱讀 37,395評論 8 86
  • 一:什么是閉包筑辨?閉包的用處? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)幸逆。在本質(zhì)上棍辕,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 9,523評論 1 52
  • 媽媽告訴我今天是感恩節(jié),它是美國的傳統(tǒng)節(jié)日, 在11月的第4個星期四,讓我們在每一天都要學會感恩秉颗。
    袁開元閱讀 196評論 0 0