【Q1】談談你對 MVVM
的理解
MVVM
是 Model-View-ViewModel
的縮寫,其中:
Model
代表數據模型,可以在Model
中定義數據修改和操作的業務邏輯。可以把Model
稱爲數據層,因爲它僅僅關注數據本身,不關心其他行爲。View
是用戶操作的界面,可以稱爲視圖層。負責視圖展現工作。當ViewModel
對Model
進行更新的時候,會通過數據綁定更新到View
。ViewModel
稱爲業務邏輯層,是數據層和視圖層通信的橋樑。View
需要什麼數據,ViewModel
要提供這個數據,有些操作也需要ViewModel
的響應。
【總結】:MVVM
模式簡化了界面與業務的依賴,解決了數據頻繁更新。在 MVVM
中,利用雙向綁定技術,使得 Model
變化時,ViewModel
會自動更新,而 ViewModel
變化時,View
也會自動更新。
【Q2】談談你對SPA的理解
SPA
應用僅在頁面首次初始化時加載相應的 HTML
,CSS
,JS
。一旦完成加載,就不會因爲用戶的操作而進行頁面的重新加載,取而代之的是利用前端路由實現內容的變換。
【優點】:
- 前後端職責更好的分離,架構清晰
- 用戶體驗好,服務器相對壓力小
【缺點】:
- 首次加載耗時久
SEO
難優化
【Q3】談談你對響應式的理解
通過數據模型(普通的 JS
對象)來驅動視圖的更新,就是響應式。
以實例的 data
選項爲例,當我們把一個普通的 JS
對象傳入 Vue
實例作爲 data
時,Vue
將遍歷這個對象的所有屬性,然後通過 Object.defineProperty
(vue 2.x)或者 Proxy
(vue 3.x)定義屬性的 getter
和 setter
,從而讓 Vue
能夠追蹤當這些屬性訪問與修改。具體是怎麼追蹤的呢?每個組件實例都對應一個 watcher
實例,它會在組件渲染的過程中把接觸過的數據記錄爲依賴,這個過程也稱爲依賴收集,然後當依賴的 setter
觸發時,就會通知 watcher
,從而使他們關聯的組件重新渲染。
【Q4】你知道 Vue
雙向數據綁定的原理嗎?
Vue
雙向數據綁定是指:數據變化更新視圖,視圖變化更新數據,例如輸入框輸入內容變化時,data
中對應的數據同步變化,data
中綁定的數據變化時,輸入框的內容同步變化。
Vue
主要通過4個部分來實現雙向數據綁定:
- 監聽器
Observer
:對數據對象進行遍歷,利用Object.defineProperty()
在屬性上都加上getter
和setter
,從而監聽數據的變化。 - 解析器
Compile
:解析模板指令,將模板中的變量都替換成數據,並將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變化就會調用更新函數進行更新。 - 訂閱者
Watcher
:訂閱者是監聽器和解析器之間通信的橋樑,主要任務是訂閱監聽器中的屬性值變化的消息,當收到屬性值變化的消息時,觸發解析器中對應的更新函數。 - 訂閱器
Dep
:訂閱器採用發佈-訂閱設計模式,用來收集訂閱者Watcher
對監聽器Observer
和 訂閱者Watcher
進行統一管理。
【Q5】爲什麼 Vue
要採用異步渲染?異步渲染是如何實現的?
如果不採用異步渲染,那麼每次更新數據都會進行重新渲染,這會對性能大打折扣,異步渲染是提高性能的手段之一。
只要監聽到數據變化,Vue
將開啓一個隊列,並緩衝在同一個事件循環中發生的所有數據變更。如果同一個 watcher
被多次觸發,只會被推入到隊列中一次。然後,在下一個事件循環中,Vue
刷新隊列並執行實際工作。Vue
內部對異步隊列常使用原生的 Promise.then
、MutationObserver
和 setImmediate
。如果環境不支持,則採用 setTimeout(fun,0)
。
【Q6】Vue
是如何檢測數組的變化
在 Vue
官方中,提供了7個操作數組的方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
用於 “響應式” 的操作數組,也就是說,通過以上7個方法改變數組的元素,這會導致 data
中的數據變化,從而導致視圖發生變化。除此以外,還可以通過 “新數組” 替換 “舊數組” 的方式來觸發視圖層的改變。但是,千萬不能通過索引直接改變一個數組,這樣不會引起視圖層的改變,只是單純的改變了數據。還有,不能通過修改數組長度來刪除元素。
Vue
將 data
中的數組進行了原型鏈重寫,指向了自己所定義的數組原型方法(也就是說上面的7個方法是Vue
重寫過的),從而實現了通過這些方法改變數組就可以通知視圖層更新的效果。
【Q7】談談你對 Vue
生命週期的理解
Vue
實例有一個完整的生命週期,也就是從開始創建、初始化數據、編譯模板、掛載DOM
、渲染、更新、卸載等一些列過程,就是生命週期。
生命週期可大致分爲 8 個階段:實例創建前/後,掛載前/後,更新前/後,銷燬前/後,分別對應8個回調鉤子。
- 實例創建前。
beforeCreate()
——vue
實例的掛載元素$el
和數據對象data
都是undefined
,還未進行初始化。 - 實例創建後。
created()
—— 完成了data
數據初始化,但未掛載。 - 掛載前。
beforeMount
——vue
實例的$el
和data
都初始化了,相關的render
函數首次被調用。實例已完成以下配置:編譯模板,把data
裏面的數據和模板生成html
。但是此時沒有掛載到html
元素上!!! - 掛載後。
mounted
——el
被$el
替換,並掛載到html
元素上。 - 更新前。
beforeUpdate
—— 在數據更新之前調用。 - 更新後。
updated
—— 此時組件DOM
已經更新,所以可以執行依賴於DOM
的操作。 - 銷燬前。
beforeDestroy
—— 實例銷燬前調用 - 銷燬後。
destroyed
—— 實例銷燬後調用。
【Q8】v-show
和 v-if
有什麼區別
兩者都用於控制 “顯示/隱藏” 元素,v-if
是真正的條件渲染,如果條件爲假,則該元素不會被掛載到 DOM
樹上,只有當條件爲真時才進行渲染。v-show
本質是控制 css
的 display
屬性來進行顯示隱藏的,不管初始條件是什麼,該元素都會被渲染。
【Q9】 Vue
是單向數據流還是雙向的?
官方是這樣來介紹的:所有的 prop
都使得其父子 prop
之間形成了一個單向下行綁定,父級 prop
的更新會向下流動到子組件中,但是反過來則不行。這也說明了不能在子組件中改變 prop
,prop
永遠都是隻讀的。如果非得改變 prop
的話,可以考慮在子組件中通過計算屬性來修改。
每次父組件發生更新時,子組件中所有的 prop
都將會刷新爲最新的值。
【Q10】組件通信的方式你知道哪些?
【方式一:props/$emit】
【父組件向子組件通信】
父組件直接通過標籤屬性的形式將數據傳遞給子組件,子組件通過實例的 props
屬性即可獲取到父組件傳遞過來的數據。
【子組件向父組件通信】
在子組件中,通過實例方法 this.$emit()
向父組件發送信息,將自己的數據傳遞給父組件。
該方法接收兩個參數,第一個是事件名,第二個是攜帶的數據。
【方式二:provide/inject】
父組件使用 provide
向下提供數據,其下所有子組件都可以通過 inject
注入,不管中間隔了多少代,都可以注入多個來自不同父級提供的數據。
provide
是 vue
實例的配置選項,它是一個對象或者一個返回對象的函數。
inject
是一個字符串數組,或一個對象。
【方式三:$parent
】
$parent
可以從一個子組件訪問父組件的實例。它是實例的屬性,可以通過實例直接訪問。
【方式四:$refs
】
通過 ref
標籤屬性可以標記一個子組件,然後通過實例的 $refs
來訪問具體的子組件實例。
【方式五:vuex
】
通過 vuex
進行狀態的統一管理。
未完,一直更