1.vue data屬性里面的getter和setter
data的每個屬性都有兩個相對應(yīng)的get和set屬性。

ES5的對象原型有兩個新的屬性__defineGetter__和__defineSetter__,專門用來給對象綁定get和set??梢赃@樣書寫:

vue雙向綁定原理是由數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式實現(xiàn)的。
vue的數(shù)據(jù)劫持是通過Object.defineProperty()來對對象的setter和getter屬性進行操作,在數(shù)據(jù)進行變動時,進行你想要的操作。
語法:Object.defineProperty(obj,prop,descriptor)
? ? ? ? ? ? 參數(shù):
? ? ? ? ? ? ? ? obj:要在其上定義屬性的對象。
? ? ? ? ? ? ? ? prop:要定義或修改的屬性名稱。
? ? ? ? ? ? ? ? descriptor:將被定義或修改的屬性描述。
? ? ? ? ? ? 返回值:
? ? ? ? ? ? ? ? ? 被傳遞給函數(shù)的對象。
也就是說他可以控制一個對象屬性的一些特有操作,比如讀寫或是否可枚舉等,這里組要看set和get。

2.vue.js計算屬性computed(getter,setter)
在Vue中,computed的屬性可以被視為是data一樣,可以讀取和設(shè)置,因此在computed中可以分成getter和setter,一般情況下沒有setter,computed預(yù)設(shè)只有g(shù)etter,也就是只能讀取,不能改變設(shè)值。
vue.js計算屬性默認只有g(shù)etter,因為是默認值所以我們也常常忽略不寫,如下代碼:

完整寫法:

計算屬性getter的出發(fā)時間:

如果我們改變上邊代碼里的2個輸入框的值firstName或則lastName,都會觸發(fā)computed以及updated(),也就是說會執(zhí)行:console.log('computed')和console.log('updated');
需要注意的是,不是說我們更改了getter里使用的變量,就會觸發(fā)computed的更新,前提是computed里的值必須要在模板里面使用才行。如果把上面代碼中的p標簽注釋掉,就算改變input的值也不會觸發(fā)computed。
2.vue生命周期。
1new Vue{
?????????router,
?????????store,
? ? ? ? ? //components: { App } vue1.0的寫法
????????????render: h => h(App) vue2.0的寫法
}).$mount('#app')
1.首先需要了解這是 es 6 的語法,表示 Vue 實例選項對象的 render 方法作為一個函數(shù),接受傳入的參數(shù) h 函數(shù),返回 h(App) 的函數(shù)調(diào)用結(jié)果。
2.其次,Vue 在創(chuàng)建 Vue 實例時,通過調(diào)用 render 方法來渲染實例的 DOM 樹。
3.最后,Vue 在調(diào)用 render 方法時,會傳入一個 createElement 函數(shù)作為參數(shù),也就是這里的 h 的實參是 createElement 函數(shù),然后 createElement 會以 APP 為參數(shù)進行調(diào)用,關(guān)于 createElement 函數(shù)的參數(shù)說明參見:Element-Arguments。

先了解一下vue的虛擬dom:
Vitual DOM是一種虛擬dom技術(shù),本質(zhì)上是基于javascript實現(xiàn)的,相對于dom對象,javascript對象更簡單,處理速度更快,dom樹的結(jié)構(gòu),屬性信息都可以很容易的用javascript對象表示:

Virtual DOM并沒有完全實現(xiàn)DOM,最主要還是保留了Element之間的層次關(guān)系和一些基本屬性。你給我一個數(shù)據(jù),我根據(jù)數(shù)據(jù)生成一個全新的Virtual DOM,然后跟我上一次生成的Virtual DOM去diff,然后通過patch方法掛載。
我們可以通過javascript對象表示的樹結(jié)構(gòu)來構(gòu)建真正的DOM樹,當(dāng)數(shù)據(jù)發(fā)聲變化時,可以直接修改這個javascript對象,接著對比修改后的對象,記錄下需要對頁面做的dom操作,然后將其應(yīng)用到真正的DOM樹,實現(xiàn)視圖的更新。
VNode生成最關(guān)鍵點是通過render函數(shù),由2種生成方式,第一種是直接在vue對象的option種添加render字段。第二種是寫一個模板或指定el跟元素,它會首先轉(zhuǎn)換成模板,經(jīng)過html語法解析器生成一個ast抽象語法樹,對語法樹做優(yōu)化,然后把語法樹轉(zhuǎn)換成代碼片段,最后通過代碼片段生成function添加到option的render字段中。
? ? ast語法優(yōu)化過程,主要做了2件事:
? ? ? ? 1.會檢測出靜態(tài)的class名和attributes,這樣它們在初始化渲染后永遠不會再被對比了。
? ? ? ? 2.會檢測出最大的靜態(tài)子樹,并且從渲染函數(shù)中萃取出來。這樣在每次重渲染時,它們會直接重用相同的vnode,同時跳過比對。
creteelment方法的功能是給一個Vnode對象添加若干個Vnode,因為整個Vritual DOM是一種樹狀結(jié)構(gòu),每個節(jié)點都可能會有若干子節(jié)點。然后創(chuàng)建一個Vnode對象,如果是一個reserved tag(html,head等一些合法的html標簽)則會創(chuàng)建普通的DOM Vnode,如果是一個component tag(通過vue注冊的自定義component),則會創(chuàng)建Component Vnode對象,它的VnodeComponentOptions不為null。
function patch(oldVnode, vnode, hydrating, removeOnly, parentElm, refElm){
創(chuàng)建好Vnode,下一步就是要把虛擬dom渲染成真正的dom,是通過patch來實現(xiàn)。patch支持3個參數(shù),其中oldNode是一個真實DOM或則一個Vnode對象,它表示當(dāng)前的VNode,vnode是VNode對象類型,它表示待替換的VNode,hydration是bool類型,它表示是否直接使用服務(wù)器端渲染的DOM元素,下面流程圖表示patch的運行邏輯:

patch運行邏輯看上去比較復(fù)雜,有2個方法createElm和patchVnode是生成dom的關(guān)鍵.
createElm方法會根據(jù)vnode的數(shù)據(jù)結(jié)構(gòu)創(chuàng)建真實的DOM節(jié)點,如果vnode有children,則會遍歷這些子節(jié)點,遞歸調(diào)用createElm方法,InsertedVnodeQueue是記錄子節(jié)點創(chuàng)建順序的隊列,每創(chuàng)建一個DOM元素就會往這個隊列中插入當(dāng)前的VNode,當(dāng)整個VNode對象全部轉(zhuǎn)換成為真實的DOM樹時,會依次調(diào)用這個隊列中的VNode hook的insert方法
1.了解vue的$mount所作的工作大體分為3部:
? ? 1.如果你的option里面沒有render函數(shù),那么,通過complieToFunctions將HTML模板編譯成可以生成VNode的Render函數(shù)。
? ? 2.new一個Watcher實例,觸發(fā)updateComponent方法。
? ? 3.生成vnode,經(jīng)過path,把vnode更新到dom上。

從上面的代碼中可以看到,首先判斷option里面有沒有render函數(shù),沒有的話,進一步判斷有沒有template,沒有的話就用dom元素的outerHTML。得到template以后干了什么呢?如下圖:

我們可以看到,調(diào)用了complieToFunction將template轉(zhuǎn)成render函數(shù)。這里面有兩個過程:
將template解析成ast語法樹。
通過ast語法樹生成render函數(shù)。
下一步就開始mountConmponet了。


從上圖可以看出,程序聲明了一個updateComponent 方法,這個是將要被Watcher實例調(diào)用的更新組件的方法。



vm:當(dāng)前的vm實例。
updateComponent 這個非常重要,用來在后面將vnode更新到dom上。
noop無意義的函數(shù)。
null option選項沒有則為null。
true主要是用來判斷是哪個watcher。因為computed計算屬性和如果你要在options里面配置watch了同樣也是使用了 new Watcher ,加上這個用以區(qū)別這三者。
if(isRenderWatcher) {
?????vm._watcher = this;
}
可以看到,如果聲明這個watcher的上下文是用來渲染視圖的,也就是說在mountComponent這里調(diào)用new Watcher的時候,才會把this賦值給_watcher。然后把 watcher push到 _watchers 里面,目的是等到組件銷毀時順便把watcher也銷毀掉。
接下來,就是賦值給 getter , this.getter = expOrFn 。還記得剛才傳過來的 updateComponent 函數(shù)么,沒錯,就是這個賦值給我 getter 。然后我們就到了:

我們可以看到,首先它執(zhí)行的是 pushTarget(this) ,pushTarget(this) 代碼如下:

也就是說如果當(dāng)前有 Dep.target 的話,就把target放到 targetStack 里面,如果沒有的話,就設(shè)為當(dāng)前的target,也就是這個watcher。 接著,就是執(zhí)行了它的 getter 屬性,也就是剛剛傳入 updateComponent 函數(shù)。
官方vue生命周期圖:

從圖可以看出在vue整個的生命周期中會有很多的鉤子函數(shù),提供給我們在vue生命周期不同的時刻進行操作。沒一個組件或則實例都會經(jīng)歷一個完整的生命周期,總共分為三個階段:初始化、運行中、銷毀。
? ? 1.實例、組件通過new Vue()創(chuàng)建出來之后會初始化事件和生命周期,然后會執(zhí)行beforeCreted鉤子函數(shù),這個時候數(shù)據(jù)還沒有掛載,只是一個空殼,無法訪問到數(shù)據(jù)和真實的dom,一般不做操作。
? ? 2.掛載數(shù)據(jù),綁定事件等等,然后執(zhí)行creted函數(shù),這個時候已經(jīng)可以只用到數(shù)據(jù),也可以更改數(shù)據(jù),在這里更改數(shù)據(jù)不會觸發(fā)updated函數(shù),在這里可以在渲染前倒數(shù)第二次更改數(shù)據(jù)的機會,不會觸發(fā)其它鉤子函數(shù),一般可以在這里做初始數(shù)據(jù)的獲取。
? ? 3.接下來開始找實例或則組建對應(yīng)的模板,編譯模板為虛擬dom放入到render函數(shù)中準備渲染,然后執(zhí)行beforeMount鉤子函數(shù),在這個函數(shù)中虛擬dom已經(jīng)創(chuàng)建完成,馬上就要渲染,在這里可以更改數(shù)據(jù),不會觸發(fā)updated,在這里可以在渲染前最后一次更改數(shù)據(jù)的機會。
? ? 4.接下來開始render,渲染出真實dom,然后執(zhí)行mounted鉤子函數(shù),此時,組件已經(jīng)出現(xiàn)在頁面中,數(shù)據(jù)、真實dom都已經(jīng)處理好了,事件都已經(jīng)掛載好了,可以在這里操作真實dom。
? ? 5.當(dāng)組件或?qū)嵗臄?shù)據(jù)更改之后,會立即執(zhí)行beforeUpdate,然后vue的虛擬dom機制就會重新構(gòu)建虛擬dom樹利用diff算法進行對比后重新渲染。
? ? 6.當(dāng)更新完成后,執(zhí)行updated,數(shù)據(jù)已經(jīng)更改完成,dom也重新render完成,可以操作更新后的虛擬dom。
? ? 7.當(dāng)經(jīng)過某種途徑調(diào)用$destroy方法后,立即執(zhí)行beforeDestroy,一般在這里做一些善后工作,例如清除計時器、清除非指令綁定的事件等等
????8.組件的數(shù)據(jù)綁定、監(jiān)聽...去掉后只剩下dom空殼,這個時候,執(zhí)行destroyed,在這里做善后工作也可以
先列出所有的鉤子函數(shù),再一一詳解:
? ? *beforeCreate
? ? *created
? ? *beforeMount
? ? *mounted
? ? *beforeUpdate
? ? *updated
? ? *beforeDsetroy
? ? *destroyed