關於Vue的一些問題以及解答
1. 什麼是MVVM?
參考文檔:https://www.cnblogs.com/goloving/p/8520030.html
MVVM是Model-View-ViewModel的簡寫。它本質上就是 MVC 的改進版。
在MVVM架構下,View 和 Model 之間並沒有直接的聯繫,而是通過ViewModel進行交互,Model 和 ViewModel 之間的交互是雙向的, 因此 View 數據的變化會同步到Model中,而Model 數據的變化也會立即反應到View 上。
Vue.js 是一個提供了 MVVM 風格的雙向數據綁定的 Javascript 庫,專注於View 層。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel負責連接 View 和 Model,保證視圖和數據的一致性,這種輕量級的架構讓前端開發更加高效、便捷。
VM 是 View 與 Model 之間的橋樑,ViewModel可以實現數據和視圖的完全分離。在視圖模型中,綁定器在視圖和數據綁定器之間進行通信。
● M(model):模型,代表真實情況的內容(一個面向對象的方法)、或表示內容(以數據爲中心的方法)的數據訪問層
● V(view):視圖---用戶界面(UI)
● Viewmodel:在vue中指vue實例對象,是一個公開公共屬性和命令的抽象的view;是一個轉值器,負責轉換Model中的數據對象,來讓對象變得更容易管理和使用。
MVVM好處:ViewModel 通過雙向數據綁定把 View 層和 Model 層連接了起來,而View 和 Model 之間的同步工作完全是自動的,無需人爲干涉,因此開發者只需關注業務邏輯,不需要手動操作DOM, 不需要關注數據狀態的同步問題,複雜的數據狀態維護完全由 MVVM 來統一管理。
2. Vue 的生命週期?生命週期函數有哪些?
每個鉤子函數能做什麼:
https://blog.csdn.net/qq_15058285/article/details/89358885、https://www.cnblogs.com/PrayLs/p/10372809.html
Vue的生命週期與鉤子函數:
1、 new Vue() 新建了一個新的 Vue 的實例對象,此時就會進入組件的創建過程
2、 Init Events & Lifecycle 初始化組件的事件和生命週期函數,當執行完這一步後,組件的生命週期函數就全部初始化好了,等待着依次去調用。
3、 beforeCreate 是組件的第一個生命週期函數,此時當前 Vue 實例的 data,el(頁面DOM結構),methods,computed都還未初始化,所以此階段什麼都做不了。很少使用該生命週期函數。
鉤子函數的作用:可以做loading。這個時候的vue實例還什麼都沒有,但是$route對象是存在的,可以根據路由信息進行重定向之類的操作。
4、 init injections & reactivity,這個階段中正在初始化 data 和 methods 中的數據和方法【導入依賴項】
5、created 這個是組件創建階段的第二個生命週期函數,此時組件的 data 和 methods 已經初始化完畢了。data和methods已經可以使用了, 但是頁面(el)還沒有渲染出來。
鉤子函數的作用:在這個生命週期函數中,我們也會經常發起Ajax請求。
6、編譯模版結構,把data上的數據拿到,並且解析執行模版結構中的指令。當所有的指令解析完畢,那麼模版頁面就被渲染到內存中了。當模版編譯完成,我們的模版頁面還沒有掛載到頁面上,只是存在於內存中,用戶看不到頁面。
6.1 判斷,初始化 Vue 實例對象的時候是否含有 el 這個屬性,如果有 el 屬性,再判斷是否有 template 屬性,如果存在 el 且 指定了 template屬性,就編譯 template 所對應的模版結構。
6.2 判斷,有 el 屬性,但是沒有指定 template 即編譯 el 所對應的的HTML結構到模版.
6.3 判斷,沒有 el 屬性,也沒有 template 屬性。Vue 實例創建完後等待手動指定el,vm.$mount(el),如未指定,則結束。
7、 beforeMount 當模版在內存中編譯完成,會立即執行實例創建階段的第三個生命週期函數,即 beforeMount,此時內存中的模版結構還沒有真正地渲染到頁面上去,此時頁面上也看不到真實的數據。此時用戶看到的只是一個模版的頁面而已。
8、Create vm.$el and replace "el" with it 這一步,正在把內存中渲染好的模版結構,替換到頁面上去。
9、 mounted 是組件創建階段最後一個生命週期函數,此時,頁面已經真正地渲染好了,用戶已經可以看到真實的頁面數據了。當這個生命週期函數執行完,組件就離開了創建階段,進入到了運行中的階段。
鉤子函數的作用:如果大家用到了一些第三方的UI插件,而且這些插件需要被初始化,必須在mounted中來初始化插件,因爲此時真實的頁面才被渲染好。此時DOM元素已經渲染完成了,依賴於DOM的代碼就放在這裏執行,比如監聽DOM事件。
10、組件運行中的生命週期函數有兩個,一個是 beforeUpdate (組件更新前)一個是 updated(組件更新後),觸發的條件是data數據發生了改變。組件運行中的生命週期函數會根據data數據的變化有選擇性地觸發0次或n次。
當數據變化,頁面更新時,首先要拿到最新的 data 數據,然後根據最新的 data 數據在內存中重新渲染一顆新的 DOM 樹,把舊的頁面移除,同時把新的DOM樹渲染出來。
當執行beforeUpdate 運行中的生命週期函數的時候,內存中的數據是最新的,但是頁面上呈現的數據還是舊的。此時可以修改vm實例中的data.
Virtual DOM re-render and patch 正在根據最新的 data 數據,重新渲染內存中的模版結構,並把渲染好的數據結構,替換到頁面上。
當執行updated的時候,此時 data 數據是最新的,同時頁面上的數據也是最新的。
11、beforeDestroy,當執行beforeDestroy的時候,組件即將被銷燬,但是還沒有真正地被銷燬,此時組件還是正常可用的。data、methods等數據或方法依然可以被正常訪問。
作用:銷燬之前還可以訪問到DOM結構 ,以及相關的數據(data)。在這個生命週期函數中我們可以將綁定的事件進行移除。
解除監聽器、綁定的事件、移除子控件。
12、destroyed,組件已經完成了銷燬,此時組件的data和methods都不可用了。
3. Vue響應式原理、雙向數據綁定的原理?
參考文檔:https://segmentfault.com/a/1190000015427628
數據雙向綁定
所謂的雙向綁定,就是view的變化能反映到ViewModel上,ViewModel的變化能同步到view上。
數據驅動
Vue.js 一個核心思想是數據驅動。所謂數據驅動是指視圖是由數據驅動生成的,對視圖的修改,不會直接操作 DOM,而是通過修改數據。相比傳統的前端開發,如使用 jQuery 等前端庫直接修改 DOM,大大簡化了代碼量,特別是當交互複雜的時候,只關心數據的修改會讓代碼的邏輯變的非常清晰,因爲 DOM 變成了數據的映射,我們所有的邏輯都是對數據的修改,而不用碰觸 DOM,這樣的代碼非常利於維護。
vue實現數據雙向綁定主要是:採用數據劫持結合發佈者-訂閱者模式的方式,通過
Object.defineProperty()
來劫持各個屬性的setter
,getter方法
,在數據變動時發佈消息給訂閱者,觸發相應監聽回調。Vue 的響應式,核心機制是 觀察者模式。
大致上是使用數據劫持和訂閱發佈實現雙向綁定。通過實例化一個Vue對象的時候,對其數據屬性遍歷,通過Object.defineProperty()給數據對象添加setter getter,並對模板做編譯生成指令對象,每個指令對象綁定一個watcher對象,然後對數據賦值的時候就會觸發setter,這時候相應的watcher對其再次求值,如果值確實發生變化了,就會通知相應的指令,調用指令的update方法,由於指令是對DOM的封裝,這時候會調用DOM的原生方法對DOM做更新,這就實現了數據驅動DOM的變化。同時vue還會對DOM做事件監聽,如果DOM發生變化,vue監聽到,就會修改相應的data。
實現代碼與原理:https://segmentfault.com/a/1190000006599500?utm_source=tag-newest
面試的時原理解釋,必要時輔以僞代碼進行說明:
核心:Vue 中雙向數據綁定的原理的核心機制是觀察者模式和發佈訂閱者模式。
1.觀察者模式就是對 VM 實例中的 data 屬性中的數據進行劫持並且監聽數據的變化,具體指修改與獲取,這裏實現觀察者模式利用了 Object.defineProperty 方法對 data 屬性的數據進行了劫持。
2.發佈和訂閱模式:發佈和訂閱模式就是把觀察者模式中劫持的數據進行統一的管理和狀態更新。VM 實例 data 屬性中的每一個數據都會生成一個 Dep 就是訂閱器,該訂閱器負責訂閱者的管理。
訂閱器有兩個功能,第一個是添加新的訂閱者,用一個數據來維護。第二是把最新變動的數據通知給每一個訂閱該數據的訂閱者,讓訂閱者進行更新。
訂閱者:閱者指的是與該數據綁定的頁面中的DOM元素或者,{{ 變量 }}模板中的變量,也就是需要與該數據同步的對象。訂閱者由解析器生成,生成的同時主動添加到對應的訂閱器中去。並且有一個 update 方法,來更新視圖數據。
3. 解析器,解析器對VM實例中的 el 屬性對應的模板信息進行逐個解析,主要負責生成訂閱者和進行相應的屬性綁定。
對數組的監控:https://segmentfault.com/a/1190000015483195?utm_source=tag-newest
Object.defineProperty 方法對數組進行監控的時候,實際上監控的是數組的地址,而不是數組值的變化,如果對數組元素進行增刪,由於數組地址沒有發生變化,實際上data屬性被劫持的set方法不會被觸發。解決方法:覆寫
數組對象中的方法或者使用Proxy進行攔截。
4. Vue組件化的理解、組件間通信?
參考文章:https://www.cnblogs.com/amunamuna/p/8872979.html
Vue組件就是可複用的 Vue 實例,所以它們與 new Vue
接收相同的選項,例如 data
、computed
、watch
、methods
以及生命週期鉤子等。僅有的例外是像 el
這樣根實例特有的選項。
父組件向子組件傳值
在 Vue 中,父組件可以使用屬性綁定的方式 (v-bind)
向子組件傳遞數據。
父組件通過向在子組件的標籤上使用 v-bind 即 <子組件標籤名 :logo="logoMsg"></子組件標籤名>向子組件傳遞數據
子組件通過在實例中使用 props 屬性(一個數組):['logo'] 來接收屬性值。
總結一下:
- 子組件在props中創建一個屬性,用以接收父組件傳過來的值
- 父組件中註冊子組件
- 在子組件標籤中添加子組件props中創建的屬性
- 把需要傳給子組件的值賦給該屬性,通過屬性傳值
子組件向父組件傳值
子組件主要通過事件機制傳遞數據給父組件。
例如在子組件的一個按鈕中綁定一個事件<button v-on:click="sendParentMsg">傳值給父組件</button>
並在子組件的methods方法中定義
methods:{
sendParentMsg(){
this.$emit('parentEvent','msgFromChild')
}
}
子組件在響應該點擊事件的函數中使用$emit來觸發一個自定義事件parentEvent,這個事件應該在父組件中定義好,並可以傳遞多個參數,例如'msgFromChild' 給父組件。
在父組件中的子標籤中監聽該自定義事件並添加一個響應該事件的處理方法。
總結一下:
- 子組件中需要以某種方式例如點擊事件的方法來觸發一個自定義事件
- 將需要傳的值作爲$emit的第二個參數,該值將作爲實參傳給響應自定義事件的方法
- 在父組件中註冊子組件並在子組件標籤上綁定對自定義事件的監聽
兄弟組件之間傳值
1.子組件傳值給父組件,父組件再傳值給兄弟組件。
2.非父子組件之間傳值,需要定義個公共的公共實例文件bus.js,作爲中間倉庫來傳值,不然路由組件之間達不到傳值的效果。
實例參考:https://blog.csdn.net/lander_xiong/article/details/79018737
狀態管理工具
爲了便於開發,Vue 推出了一個狀態管理工具 Vuex,可以很方便實現組件之間的參數傳遞。
1.state
vuex中的數據源,我們需要保存的數據就保存在這裏,可以在頁面通過 this.$store.state來獲取我們定義的數據。
2.getters
Getter相當於vue中的computed計算屬性,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被重新計算,這裏我們可以通過定義vuex的Getter來獲取,Getters 可以用於監聽、state中的值的變化,返回計算後的結果。
3.Mutations
數據我們在頁面是獲取到了,但是如果我們需要修改count值怎麼辦?如果需要修改store中的值唯一的方法就是提交mutation來修改。
5.v-bind、v-on、v-model
v-
前綴作爲一種視覺提示,用來識別模板中 Vue 特定的特性。當你在使用 Vue.js 爲現有標籤添加動態行爲 (dynamic behavior) 時,v-
前綴很有幫助,然而,對於一些頻繁用到的指令來說,就會感到使用繁瑣。同時,在構建由 Vue 管理所有模板的單頁面應用程序 (SPA - single page application)時,v-
前綴也變得沒那麼重要了。因此,Vue 爲 v-bind
和 v-on
這兩個最常用的指令,提供了特定簡寫:
v-bind
縮寫
v-bind指令:用於給html標籤設置屬性。
<!-- 完整語法 -->
<a v-bind:href="url">...</a>
<!-- 縮寫 -->
<a :href="url">...</a>
v-on
縮寫
v-on指令:用來綁定事件的。
<!-- 完整語法 -->
<a v-on:click="doSomething">...</a>
<!-- 縮寫 -->
<a @click="doSomething">...</a>
v-on中的事件修飾符:
(1).stop :阻止事件冒泡
<div @click.stop="test">
(2).prevent : .prevent修飾符,其作用就是阻止組件本來應該發生的事件,轉而去執行自己定義的事件
<a href="http://xxx.com" @click.prevent="test">跳轉</a>
阻止a鏈接的跳轉事件,轉而執行 test 事件。
(3).caption
使用.capture修飾符時,網頁就會按照捕獲的方式觸發函數,也就是從外向內執行,默認是冒泡
(4).once
加上此修飾符之後相應的函數只能觸發一次,無論你點擊多少下,函數就只觸發一次。
(5).self
當前元素自身時觸發處理函數時纔會觸發函數,原理:是根據event.target確定是否當前元素本身,來決定是否觸發的事件/函數
v-model
v-model是一個指令,限制在<input>、<select>、<textarea>、components中使用,取代 input 監聽 change 事件。它是一個語法糖,功能是監聽 change 事件的同時綁定表單元素的 value 屬性。它能輕鬆實現表單輸入和應用狀態之間的雙向綁定(改變其中一個值,另一個值也一起同步更新)。
6. watch與computed屬性?
computed和watch屬性:https://blog.csdn.net/joseydon/article/details/81157867
computed屬性實現原理:https://blog.csdn.net/w18478272407/article/details/85079746
computed:對變量有複雜邏輯操作的模版使用計算屬性,computed 屬性會基於它所依賴的數據進行緩存,只有在它所依賴的數據發生變化時,纔會重新取值。
watch:監聽屬性變化,屬性發生變化才執行監聽的函數。
7. v-show 和 v-if 的區別
共同點:在 Vue 中,v-show 和 v-if 都是控制元素顯示和隱藏的屬性。v-show、v-if = true 元素顯示,v-show、v-if = false 元素隱藏。
不同點:
v-show 是直接控制元素的 display 屬性,v-show = false的時候,元素的 display:none,無論爲何值,v-show控制的元素都會編譯到模板中。v-show本質上是在控制元素的css屬性。
v-if 是動態的向DOM樹內添加或者刪除DOM元素。如果v-if初始值爲false,該元素則不會編譯到模板裏。
總結:
v-show 有更高的首次渲染開銷,而 v-if 的首次渲染開銷要小的多;
v-if 有更高的切換開銷,v-show 切換開銷小,如果元素在運行中需要經常切換顯示和隱藏使用v-show開銷比較小。
8. Vue-Router 原理
更新視圖但不重新請求頁面。
https://juejin.im/post/5bc6eb875188255c9c755df2
9. Vue虛擬DOM和diff算法
只進行同層比較