(十二)前端-VUE面試
Vue相關
1.vue生命週期
什麼是Vue生命週期?
Vue 例項從建立到銷燬的過程,就是生命週期。也就是從開始建立、初始化資料、編譯模板、掛載Dom→渲染、更新→渲染、解除安裝等一系列過程,我們稱這是 Vue 的生命週期
Vue****生命週期的作用是什麼?
它的生命週期中有多個事件鉤子,讓我們在控制整個Vue例項的過程時更容易形成好的邏輯
Vue****生命週期總共有幾個階段?
它可以總共分為8個階段:建立前/後, 載入前/後,更新前/後,銷燬前/銷燬後
第一次頁面載入會觸發哪幾個鉤子?
第一次頁面載入時會觸發 beforeCreate, created, beforeMount, mounted 這幾個鉤子
DOM****渲染在哪個週期中就已經完成?
DOM 渲染在 mounted 中就已經完成了
每個生命週期適合哪些場景?
生命週期鉤子的一些使用方法:
beforecreate : 可以在這加個loading事件,在載入例項時觸發
created : 初始化完成時的事件寫在這裡,如在這結束loading事件,非同步請求也適宜在這裡呼叫
mounted : 掛載元素,獲取到DOM節點
updated : 如果對資料統一處理,在這裡寫上相應函式
beforeDestroy : 可以做一個確認停止事件的確認框
nextTick : 更新資料後立即操作dom
· beforeCreate階段:vue例項的掛載元素el和資料物件data都是undefined,還沒有初始化。
· created階段:vue例項的資料物件data有了,可以訪問裡面的資料和方法,未掛載到DOM,el還沒有
· beforeMount階段:vue例項的el和data都初始化了,但是掛載之前為虛擬的dom節點
· mounted階段:vue例項掛載到真實DOM上,就可以通過DOM獲取DOM節點
· beforeUpdate階段:響應式資料更新時呼叫,發生在虛擬DOM打補丁之前,適合在更新之前訪問現有的DOM,比如手動移除已新增的事件監聽器
· updated階段:虛擬DOM重新渲染和打補丁之後呼叫,組成新的DOM已經更新,避免在這個鉤子函式中操作資料,防止死迴圈
· beforeDestroy階段:例項銷燬前呼叫,例項還可以用,this能獲取到例項,常用於銷燬定時器,解綁事件
· destroyed階段:例項銷燬後呼叫,呼叫後所有事件監聽器會被移除,所有的子例項都會被銷燬
2.v-show與v-if區別
v-show是css切換,v-if是完整的銷燬和重新建立
使用 頻繁切換時用v-show,執行時較少改變時用v-if
v-if=‘false’ v-if是條件渲染,當false的時候不會渲染
3.MVVM相關
vue採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty
劫持data屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。
MVVM
M - Model,Model 代表資料模型,也可以在 Model 中定義資料修改和操作的業務邏輯
V - View,View 代表 UI 元件,它負責將資料模型轉化為 UI 展現出來
VM - ViewModel,ViewModel 監聽模型資料的改變和控制檢視行為、處理使用者互動,簡單理解就是一個同步 View 和 Model 的物件,連線 Model 和 View
![image.png](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image024.png)
· View 接收使用者互動請求
· View 將請求轉交給ViewModel
· ViewModel 操作Model資料更新
· Model 更新完資料,通知ViewModel資料發生變化
· ViewModel 更新View資料
MVC
· View 接受使用者互動請求
· View 將請求轉交給Controller處理
· Controller 操作Model進行資料更新儲存
· 資料更新儲存之後,Model會通知View更新
· View 更新變化資料使使用者得到反饋
![image.png](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image026.png)
**
MVVM****模式和MVC有些類似,但有以下不同**
· ViewModel 替換了 Controller,在UI層之下
· ViewModel 向 View 暴露它所需要的資料和指令物件
· ViewModel 接收來自 Model 的資料
概括起來,MVVM是由MVC發展而來,通過在Model之上而在View之下增加一個非視覺的元件將來自Model的資料對映到View中。
4.說說你對 SPA 單頁面的理解,它的優缺點分別是什麼?
SPA( single-page application )僅在 Web 頁面初始化時載入相應的 HTML、JavaScript 和 CSS。一旦頁面載入完成,SPA 不會因為使用者的操作而進行頁面的重新載入或跳轉;取而代之的是利用路由機制實現 HTML 內容的變換,UI 與使用者的互動,避免頁面的重新載入。
優點:
· 使用者體驗好、快,內容的改變不需要重新載入整個頁面,避免了不必要的跳轉和重複渲染;
· 基於上面一點,SPA 相對對伺服器壓力小;
· 前後端職責分離,架構清晰,前端進行互動邏輯,後端負責資料處理;
缺點:
· 初次載入耗時多:為實現單頁 Web 應用功能及顯示效果,需要在載入頁面的時候將 JavaScript、CSS 統一載入,部分頁面按需載入;
· 前進後退路由管理:由於單頁應用在一個頁面中顯示所有的內容,所以不能使用瀏覽器的前進後退功能,所有的頁面切換需要自己建立堆疊管理;
· SEO 難度較大:由於所有的內容都在一個頁面中動態替換顯示,所以在 SEO 上其有著天然的弱勢。
5、computed 和 watch 的區別和運用的場景?
computed****: 是計算屬性,依賴其它屬性值,並且 computed 的值有快取,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值;
watch****: 更多的是「觀察」的作用,類似於某些資料的監聽回撥 ,每當監聽的資料變化時都會執行回撥進行後續操作;
運用場景:
· 當我們需要進行數值計算,並且依賴於其它資料時,應該使用 computed,因為可以利用 computed 的快取特性,避免每次獲取值時,都要重新計算;
· 當我們需要在資料變化時執行非同步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行非同步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,並在我們得到最終結果前,設定中間狀態。這些都是計算屬性無法做到的。
computed****的原理?
computed 本質是一個惰性求值的觀察者。
computed 內部實現了一個惰性的 watcher,也就是 computed watcher,computed watcher 不會立刻求值,同時持有一個 dep 例項。
其內部通過 this.dirty 屬性標記計算屬性是否需要重新求值。
當 computed 的依賴狀態發生改變時,就會通知這個惰性的 watcher,
computed watcher 通過 this.dep.subs.length 判斷有沒有訂閱者,
有的話,會重新計算,然後對比新舊值,如果變化了,會重新渲染。 (Vue 想確保不僅僅是計算屬性依賴的值發生變化,而是當計算屬性最終計算的值發生變化時才會觸發渲染 watcher 重新渲染,本質上是一種優化。)
沒有的話,僅僅把 this.dirty = true。 (當計算屬性依賴於其他資料時,屬性並不會立即重新計算,只有之後其他地方需要讀取屬性的時候,它才會真正計算,即具備 lazy(懶計算)特性。)
6.v-model 的原理
我們在 vue 專案中主要使用 v-model 指令在表單 input、textarea、select 等元素上建立雙向資料繫結,我們知道 v-model 本質上不過是語法糖,v-model 在內部為不同的輸入元素使用不同的屬性並丟擲不同的事件:
· text 和 textarea 元素使用 value 屬性和 input 事件;
· checkbox 和 radio 使用 checked 屬性和 change 事件;
· select 欄位將 value 作為 prop 並將 change 作為事件。
<input v-model='something'>
相當於
<input v-bind:value="something" v-on:input="something = $event.target.value">
![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image028.png)
7.VUE和REACT 的區別?
react整體是函式式的思想,把元件設計成純元件,狀態和邏輯通過引數傳入,所以在react中,是單向資料流;
vue的思想是響應式的,也就是基於是資料可變的,通過對每一個屬性建立Watcher來監聽,當屬性變化的時候,響應式的更新對應的虛擬dom。
核心思想:
vue的整體思想仍然是擁抱經典的html(結構)+css(表現)+js(行為)的形式,vue鼓勵開發者使用template模板,並提供指令供開發者使用(v-if、v-show、v-for等等),因此在開發vue應用的時候會有一種在寫經典web應用(結構、表現、行為分離)的感覺。另一方面,在針對元件資料上,vue2.0通過Object.defineProperty
對資料做到了更細緻的監聽,精準實現元件級別的更新。
react整體上是函式式的思想,元件使用jsx語法,all in js,將html與css全都融入javaScript,jsx語法相對來說更加靈活,我一開始剛轉過來也不是很適應,感覺寫react應用感覺就像是在寫javaScript。當元件呼叫setState或props變化的時候,元件內部render會重新渲染,子元件也會隨之重新渲染,可以通過shouldComponentUpdate或者PureComponent可以避免不必要的重新渲染(個人感覺這一點上不如vue做的好)。
具體可參考掘金文章(關於Vue*和React*的一些對比及個人思考:https://juejin.im/post/5e153e096fb9a048297390c1*)*
8. 為什麼在 Vue3.0 採用了 Proxy,拋棄Object.defineProperty?
Object.defineProperty 只能劫持物件的屬性,因此我們需要對每個物件的每個屬性進行遍歷。Vue 2.x 裡,是通過 遞迴 + 遍歷 data 物件來實現對資料的監控的,如果屬性值也是物件那麼需要深度遍歷,顯然如果能劫持一個完整的物件是才是更好的選擇。
Proxy 可以劫持整個物件,並返回一個新的物件。Proxy 不僅可以代理物件,還可以代理陣列。還可以代理動態增加的屬性。
Proxy 的優勢如下:
· Proxy 可以直接監聽物件而非屬性;
· Proxy 可以直接監聽陣列的變化;
· Proxy 有多達 13 種攔截方法,不限於 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具備的;
· Proxy 返回的是一個新物件,我們可以只操作新的物件達到目的,而 Object.defineProperty 只能遍歷物件屬性直接修改;
· Proxy 作為新標準將受到瀏覽器廠商重點持續的效能優化,也就是傳說中的新標準的效能紅利;
Object.defineProperty 的優勢如下:
· 相容性好,支援 IE9,而 Proxy 的存在瀏覽器相容性問題,而且無法用 polyfill 磨平,因此 Vue 的作者才宣告需要等到下個大版本( 3.0 )才能用 Proxy 重寫。
9. Vue 元件 data 為什麼必須是函式 ?
因為元件是可以複用的,JS 裡物件是引用關係,如果元件 data 是一個物件,那麼子元件中的 data 屬性值會互相汙染,產生副作用。
所以一個元件的 data 選項必須是一個函式,因此每個例項可以維護一份被返回物件的獨立的拷貝。new Vue 的例項是不會被複用的,因此不存在以上問題。
10、談談你對 keep-alive 的瞭解?
keep-alive 是 Vue 內建的一個元件,可以使被包含的元件保留狀態,避免重新渲染 ,其有以下特性:
o 一般結合路由和動態元件一起使用,用於快取元件;
o 提供 include 和 exclude 屬性,兩者都支援字串或正則表示式, include 表示只有名稱匹配的元件會被快取,exclude 表示任何名稱匹配的元件都不會被快取 ,其中 exclude 的優先順序比 include 高;
o 對應兩個鉤子函式 activated 和 deactivated ,當元件被啟用時,觸發鉤子函式 activated,當元件被移除時,觸發鉤子函式 deactivated。
11、Vue 元件間通訊有哪幾種方式?
(1)**props / $emit**
適用 父子元件通訊
這種方法是 Vue 元件的基礎,相信大部分同學耳聞能詳,所以此處就不舉例展開介紹。
(2)**ref**
與 **$parent / $children**
適用 父子元件通訊
· ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子元件上,引用就指向元件例項
· $parent
/ $children
:訪問父 / 子例項
(3)**EventBus** ``**(**``**$emit / $on**``**)**
適用於 父子、隔代、兄弟元件通訊
這種方法通過一個空的 Vue 例項作為中央事件匯流排(事件中心),用它來觸發事件和監聽事件,從而實現任何元件間的通訊,包括父子、隔代、兄弟元件
(4)**$attrs**
/**$listeners**
適用於 隔代元件通訊
· $attrs
:包含了父作用域中不被 prop 所識別 (且獲取) 的特性繫結 ( class 和 style 除外 )。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結 ( class 和 style 除外 ),並且可以通過 v-bind="$attrs"
傳入內部元件。通常配合 inheritAttrs 選項一起使用。
· $listeners
:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners"
傳入內部元件
(5)**provide / inject**
適用於 隔代元件通訊
祖先元件中通過 provider 來提供變數,然後在子孫元件中通過 inject 來注入變數。 provide / inject API 主要解決了跨級元件間的通訊問題,不過它的使用場景,主要是子元件獲取上級元件的狀態,跨級元件間建立了一種主動提供與依賴注入的關係。
(6)Vuex 適用於 父子、隔代、兄弟元件通訊
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。每一個 Vuex 應用的核心就是 store(倉庫)。“store” 基本上就是一個容器,它包含著你的應用中大部分的狀態 ( state )。
· Vuex 的狀態儲存是響應式的。當 Vue 元件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的元件也會相應地得到高效更新。
· 改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化。
12.請介紹一下你對vuex的理解?
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。每一個 Vuex 應用的核心就是 store(倉庫)。“store” 基本上就是一個容器,它包含著你的應用中大部分的狀態 ( state )。
(1)Vuex 的狀態儲存是響應式的。當 Vue 元件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的元件也會相應地得到高效更新。
(2)改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化。
主要包括以下幾個模組:
· State:定義了應用狀態的資料結構,可以在這裡設定預設的初始狀態。
· Getter:允許元件從 Store 中獲取資料,mapGetters 輔助函式僅僅是將 store 中的 getter 對映到區域性計算屬性。
· Mutation:是唯一更改 store 中狀態的方法,且必須是同步函式。
· Action:用於提交 mutation,而不是直接變更狀態,可以包含任意非同步操作。
· Module:允許將單一的 Store 拆分為多個 store 且同時儲存在單一的狀態樹中。
VUEX****實現原理? (課程中的程式碼)
13.請介紹一下你對vue-router的理解?
vue-router****實現原理? (課程中的程式碼)
vue-router 有 3 種路由模式:hash、history、abstract,
l hash: 使用 URL hash 值來作路由。支援所有瀏覽器,包括不支援 HTML5 History Api 的瀏覽器;
l history : 依賴 HTML5 History API 和伺服器配置。具體可以檢視 HTML5 History 模式;
l abstract : 支援所有 JavaScript 執行環境,如 Node.js 伺服器端。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式.
1****)hash 模式的實現原理
早期的前端路由的實現就是基於 location.hash 來實現的。其實現原理很簡單,location.hash 的值就是 URL 中 # 後面的內容。比如下面這個網站,它的 location.hash 的值為 '#search':
hash 路由模式的實現主要是基於下面幾個特性:
l URL 中 hash 值只是客戶端的一種狀態,也就是說當向伺服器端發出請求時,hash 部分不會被髮送;
l hash 值的改變,都會在瀏覽器的訪問歷史中增加一個記錄。因此我們能通過瀏覽器的回退、前進按鈕控制hash 的切換;
l 可以通過 a 標籤,並設定 href 屬性,當用戶點選這個標籤後,URL 的 hash 值會發生改變;或者使用 JavaScript 來對 loaction.hash 進行賦值,改變 URL 的 hash 值;
l 我們可以使用 hashchange 事件來監聽 hash 值的變化,從而對頁面進行跳轉(渲染)。
(2)history 模式的實現原理
HTML5 提供了 History API 來實現 URL 的變化。其中做最主要的 API 有以下兩個:history.pushState() 和 history.repalceState()。這兩個 API 可以在不進行重新整理的情況下,操作瀏覽器的歷史紀錄。唯一不同的是,前者是新增一個歷史記錄,後者是直接替換當前的歷史記錄,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的實現主要基於存在下面幾個特性:
l pushState 和 repalceState 兩個 API 來操作實現 URL 的變化 ;
l 我們可以使用 popstate 事件來監聽 url 的變化,從而對頁面進行跳轉(渲染);
l history.pushState() 或 history.replaceState() 不會觸發 popstate 事件,這時我們需要手動觸發頁面跳轉(渲染)。
導航鉤子函式(導航守衛)?
l 全域性守衛
\1. router.beforeEach 全域性前置守衛 進入路由之前
\2. router.beforeResolve 全域性解析守衛(2.5.0+) 在beforeRouteEnter呼叫之後呼叫
\3. router.afterEach 全域性後置鉤子 進入路由之後
// main.js 入口檔案
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => {
next();
});
router.beforeResolve((to, from, next) => {
next();
});
router.afterEach((to, from) => {
console.log('afterEach 全域性後置鉤子');
});
l 路由獨享的守衛 你可以在路由配置上直接定義 beforeEnter 守衛
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
l 元件內的守衛 你可以在路由元件內直接定義以下路由導航守衛
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該元件的對應路由被 confirm 前呼叫
// 不!能!獲取元件例項 `this`
// 因為當守衛執行前,元件例項還沒被建立
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該元件被複用時呼叫
// 舉例來說,對於一個帶有動態引數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 元件,因此元件例項會被複用。而這個鉤子就會在這個情況下被呼叫。
// 可以訪問元件例項 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該元件的對應路由時呼叫,我們用它來禁止使用者離開
// 可以訪問元件例項 `this`
// 比如還未儲存草稿,或者在使用者離開前,
將setInterval銷燬,防止離開之後,定時器還在呼叫。
}
}
14、Vue 中的 key 有什麼作用?
Vue 中 key 的作用是:key 是為 Vue 中 vnode 的唯一標記,通過這個 key,我們的 diff 操作可以更準確、更快速
更準確:因為帶 key 就不是就地複用了,在 sameNode 函式 a.key === b.key
對比中可以避免就地複用的情況。所以會更加準確。
更快速:利用 key 的唯一性生成 map 物件來獲取對應節點,比遍歷方式更快
15.ref的作用
\1. 獲取dom元素this.$refs.box
\2. 獲取子元件中的datathis.$refs.box.msg
\3. 呼叫子元件中的方法this.$refs.box.open()
30 道 Vue 面試題,內含詳細講解(涵蓋入門到精通,自測 Vue 掌握程度)
[Vue 專案效能優化 — 實踐指南](