1. 程式人生 > 實用技巧 >(十二)前端-VUE面試

(十二)前端-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 掌握程度)

面試完50個人後我寫下這篇總結

公司要求會使用框架vue,面試題會被問及哪些?

[Vue 專案效能優化 — 實踐指南](