2022年vue高頻面試題分享(附答案分析)

2022-08-01 22:00:34
本篇文章給大家總結一些值得收藏的2022年精選高頻面試題(附答案)。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

Vue-router 導航守衛有哪些

  • 全域性前置/勾點:beforeEach、beforeResolve、afterEach
  • 路由獨享的守衛:beforeEnter
  • 元件內的守衛:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

(學習視訊分享:)

為什麼在 Vue3.0 採用了 Proxy,拋棄了 Object.defineProperty?

Object.defineProperty 本身有一定的監控到陣列下標變化的能力,但是在 Vue 中,從效能/體驗的價效比考慮,尤大大就棄用了這個特性(Vue 為什麼不能檢測陣列變動 )。為了解決這個問題,經過 vue 內部處理後可以使用以下幾種方法來監聽陣列
push();
pop();
shift();
unshift();
splice();
sort();
reverse();

由於只針對了以上 7 種方法進行了 hack 處理,所以其他陣列的屬性也是檢測不到的,還是具有一定的侷限性。

Object.defineProperty 只能劫持物件的屬性,因此我們需要對每個物件的每個屬性進行遍歷。Vue 2.x 裡,是通過 遞迴 + 遍歷 data 物件來實現對資料的監控的,如果屬性值也是物件那麼需要深度遍歷,顯然如果能劫持一個完整的物件是才是更好的選擇。

Proxy 可以劫持整個物件,並返回一個新的物件。Proxy 不僅可以代理物件,還可以代理陣列。還可以代理動態增加的屬性。

v-for 為什麼要加 key

如果不使用 key,Vue 會使用一種最大限度減少動態元素並且儘可能的嘗試就地修改/複用相同型別元素的演演算法。key 是為 Vue 中 vnode 的唯一標記,通過這個 key,我們的 diff 操作可以更準確、更快速

  • 更準確:因為帶 key 就不是就地複用了,在 sameNode 函數 a.key === b.key 對比中可以避免就地複用的情況。所以會更加準確。

  • 更快速:利用 key 的唯一性生成 map 物件來獲取對應節點,比遍歷方式更快

如何從真實DOM到虛擬DOM

涉及到Vue中的模板編譯原理,主要過程:

  • 將模板轉換成 ast 樹, ast 用物件來描述真實的JS語法(將真實DOM轉換成虛擬DOM)

  • 優化樹

  • ast 樹生成程式碼

為什麼Vue採用非同步渲染呢?

Vue 是元件級更新,如果不採用非同步更新,那麼每次更新資料都會對當前元件進行重新渲染,所以為了效能, Vue 會在本輪資料更新後,在非同步更新檢視。核心思想 nextTick

dep.notify() 通知 watcher進行更新, subs[i].update 依次呼叫 watcher 的 update queueWatcher 將watcher 去重放入佇列, nextTick( flushSchedulerQueue )在下一tick中重新整理watcher佇列(非同步)。

為什麼vue元件中data必須是一個函數?

物件為參照型別,當複用元件時,由於資料物件都指向同一個data物件,當在一個元件中修改data時,其他重用的元件中的data會同時被修改;而使用返回物件的函數,由於每次返回的都是一個新物件(Object的範例),參照地址不同,則不會出現這個問題。

MVC 和 MVVM 區別

MVC

MVC 全名是 Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範

  • Model(模型):是應用程式中用於處理應用程式資料邏輯的部分。通常模型物件負責在資料庫中存取資料
  • View(檢視):是應用程式中處理資料顯示的部分。通常檢視是依據模型資料建立的
  • Controller(控制器):是應用程式中處理使用者互動的部分。通常控制器負責從檢視讀取資料,控制使用者輸入,並向模型傳送資料

MVC 的思想:一句話描述就是 Controller 負責將 Model 的資料用 View 顯示出來,換句話說就是在 Controller 裡面把 Model 的資料賦值給 View。

MVVM

MVVM 新增了 VM 類

  • ViewModel 層:做了兩件事達到了資料的雙向繫結 一是將【模型】轉化成【檢視】,即將後端傳遞的資料轉化成所看到的頁面。實現的方式是:資料繫結。二是將【檢視】轉化成【模型】,即將所看到的頁面轉化成後端的資料。實現的方式是:DOM 事件監聽。

MVVM 與 MVC 最大的區別就是:它實現了 View 和 Model 的自動同步,也就是當 Model 的屬性改變時,我們不用再自己手動操作 Dom 元素,來改變 View 的顯示,而是改變屬性後該屬性對應 View 層顯示會自動改變(對應Vue資料驅動的思想)

整體看來,MVVM 比 MVC 精簡很多,不僅簡化了業務與介面的依賴,還解決了資料頻繁更新的問題,不用再用選擇器操作 DOM 元素。因為在 MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也觀察不到 View,這種低耦合模式提高程式碼的可重用性

注意:Vue 並沒有完全遵循 MVVM 的思想 這一點官網自己也有說明

那麼問題來了 為什麼官方要說 Vue 沒有完全遵循 MVVM 思想呢?

  • 嚴格的 MVVM 要求 View 不能和 Model 直接通訊,而 Vue 提供了$refs 這個屬性,讓 Model 可以直接操作 View,違反了這一規定,所以說 Vue 沒有完全遵循 MVVM。

Vue 為什麼要用 vm.$set() 解決物件新增屬性不能響應的問題 ?你能說說如下程式碼的實現原理麼?

1)Vue為什麼要用vm.$set() 解決物件新增屬性不能響應的問題

  • Vue使用了Object.defineProperty實現雙向資料繫結

  • 在初始化範例時對屬性執行 getter/setter 轉化

  • 屬性必須在data物件上存在才能讓Vue將它轉換為響應式的(這也就造成了Vue無法檢測到物件屬性的新增或刪除)

所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)

2)接下來我們看看框架本身是如何實現的呢?

Vue 原始碼位置:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 為陣列  
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改陣列的長度, 避免索引>陣列長度導致splcie()執行有誤
    target.length = Math.max(target.length, key)
    // 利用陣列的splice變異方法觸發響應式  
    target.splice(key, 1, val)
    return val
  }
  // key 已經存在,直接修改屬性值  
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 本身就不是響應式資料, 直接賦值
  if (!ob) {
    target[key] = val
    return val
  }
  // 對屬性進行響應式處理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

我們閱讀以上原始碼可知,vm.$set 的實現原理是:

  • 如果目標是陣列,直接使用陣列的 splice 方法觸發相應式;

  • 如果目標是物件,會先判讀屬性是否存在、物件是否是響應式,

  • 最終如果要對屬性進行響應式處理,則是通過呼叫 defineReactive 方法進行響應式處理

defineReactive 方法就是 Vue 在初始化物件時,給物件屬性採用 Object.defineProperty 動態新增 getter 和 setter 的功能所呼叫的方法

Vue3.0 和 2.0 的響應式原理區別

Vue3.x 改用 Proxy 替代 Object.defineProperty。因為 Proxy 可以直接監聽物件和陣列的變化,並且有多達 13 種攔截方法。

相關程式碼如下

import { mutableHandlers } from "./baseHandlers"; // 代理相關邏輯
import { isObject } from "./util"; // 工具方法

export function reactive(target) {
  // 根據不同引數建立不同響應式物件
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
  if (!isObject(target)) {
    return target;
  }
  const observed = new Proxy(target, baseHandler);
  return observed;
}

const get = createGetter();
const set = createSetter();

function createGetter() {
  return function get(target, key, receiver) {
    // 對獲取的值進行放射
    const res = Reflect.get(target, key, receiver);
    console.log("屬性獲取", key);
    if (isObject(res)) {
      // 如果獲取的值是物件型別,則返回當前物件的代理物件
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {
      console.log("屬性新增", key, value);
    } else if (hasChanged(value, oldValue)) {
      console.log("屬性值被修改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 當獲取屬性時呼叫此方法
  set, // 當修改屬性時呼叫此方法
};

Vue模版編譯原理知道嗎,能簡單說一下嗎?

簡單說,Vue的編譯過程就是將template轉化為render函數的過程。會經歷以下階段:

  • 生成AST樹
  • 優化
  • codegen

首先解析模版,生成AST語法樹(一種用JavaScript物件的形式來描述整個模板)。 使用大量的正規表示式對模板進行解析,遇到標籤、文字的時候都會執行對應的勾點進行相關處理。

Vue的資料是響應式的,但其實模板中並不是所有的資料都是響應式的。有一些資料首次渲染後就不會再變化,對應的DOM也不會變化。那麼優化過程就是深度遍歷AST樹,按照相關條件對樹節點進行標記。這些被標記的節點(靜態節點)我們就可以跳過對它們的比對,對執行時的模板起到很大的優化作用。

編譯的最後一步是將優化後的AST樹轉換為可執行的程式碼

MVVM的優缺點?

優點:

  • 分離檢視(View)和模型(Model),降低程式碼耦合,提⾼檢視或者邏輯的重⽤性: ⽐如檢視(View)可以獨⽴於Model變化和修改,⼀個ViewModel可以繫結不同的"View"上,當View變化的時候Model不可以不變,當Model變化的時候View也可以不變。你可以把⼀些檢視邏輯放在⼀個ViewModel⾥⾯,讓很多view重⽤這段檢視邏輯
  • 提⾼可測試性: ViewModel的存在可以幫助開發者更好地編寫測試程式碼
  • ⾃動更新dom: 利⽤雙向繫結,資料更新後檢視⾃動更新,讓開發者從繁瑣的⼿動dom中解放

缺點:

  • Bug很難被偵錯: 因為使⽤雙向繫結的模式,當你看到界⾯異常了,有可能是你View的程式碼有Bug,也可能是Model的程式碼有問題。資料繫結使得⼀個位置的Bug被快速傳遞到別的位置,要定位原始出問題的地⽅就變得不那麼容易了。另外,資料繫結的宣告是指令式地寫在View的模版當中的,這些內容是沒辦法去打斷點debug的
  • ⼀個⼤的模組中model也會很⼤,雖然使⽤⽅便了也很容易保證了資料的⼀致性,當時⻓期持有,不釋放記憶體就造成了花費更多的記憶體
  • 對於⼤型的圖形應⽤程式,檢視狀態較多,ViewModel的構建和維護的成本都會⽐較⾼。

Vue data 中某一個屬性的值發生改變後,檢視會立即同步執行重新渲染嗎?

不會立即同步執行重新渲染。Vue 實現響應式並不是資料發生變化之後 DOM 立即變化,而是按一定的策略進行 DOM 的更新。Vue 在更新 DOM 時是非同步執行的。只要偵聽到資料變化, Vue 將開啟一個佇列,並緩衝在同一事件迴圈中發生的所有資料變更。

如果同一個watcher被多次觸發,只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 DOM 操作是非常重要的。然後,在下一個的事件迴圈tick中,Vue 重新整理佇列並執行實際(已去重的)工作。

diff演演算法

時間複雜度: 個樹的完全 diff 演演算法是一個時間複雜度為 O(n*3) ,vue進行優化轉化成 O(n)

理解:

  • 最小量更新, key 很重要。這個可以是這個節點的唯一標識,告訴 diff 演演算法,在更改前後它們是同一個DOM節點

    • 擴充套件 v-for 為什麼要有 key ,沒有 key 會暴力複用,舉例子的話隨便說一個比如移動節點或者增加節點(修改DOM),加 key 只會移動減少操作DOM。
  • 只有是同一個虛擬節點才會進行精細化比較,否則就是暴力刪除舊的,插入新的。
  • 只進行同層比較,不會進行跨層比較。

diff演演算法的優化策略:四種命中查詢,四個指標

  1. 舊前與新前(先比開頭,後插入和刪除節點的這種情況)
  2. 舊後與新後(比結尾,前插入或刪除的情況)
  3. 舊前與新後(頭與尾比,此種發生了,涉及移動節點,那麼新前指向的節點,移動到舊後之後)
  4. 舊後與新前(尾與頭比,此種發生了,涉及移動節點,那麼新前指向的節點,移動到舊前之前)

--- 問完上面這些如果都能很清楚的話,基本O了 ---

以下的這些簡單的概念,你肯定也是沒有問題的啦?

Vue的優點

  • 輕量級框架:只關注檢視層,是一個構建資料的檢視集合,大小隻有幾十 kb
  • 簡單易學:國人開發,中文檔案,不存在語言障礙 ,易於理解和學習;
  • 雙向資料繫結:保留了 angular 的特點,在資料操作方面更為簡單;
  • 元件化:保留了 react 的優點,實現了 html 的封裝和重用,在構建單頁面應用方面有著獨特的優勢;
  • 檢視,資料,結構分離:使資料的更改更為簡單,不需要進行邏輯程式碼的修改,只需要運算元據就能完成相關操作;
  • 虛擬DOM:dom 操作是非常耗費效能的,不再使用原生的 dom 操作節點,極大解放 dom 操作,但具體操作的還是 dom 不過是換了另一種方式;
  • 執行速度更快:相比較於 react 而言,同樣是操作虛擬 dom,就效能而言, vue 存在很大的優勢。

vue-router 路由勾點函數是什麼 執行順序是什麼

路由勾點的執行流程, 勾點函數種類有:全域性守衛、路由守衛、元件守衛

完整的導航解析流程:

  • 導航被觸發。

  • 在失活的元件裡呼叫 beforeRouteLeave 守衛。

  • 呼叫全域性的 beforeEach 守衛。

  • 在重用的元件裡呼叫 beforeRouteUpdate 守衛 (2.2+)。

  • 在路由設定裡呼叫 beforeEnter。

  • 解析非同步路由元件。

  • 在被啟用的元件裡呼叫 beforeRouteEnter。

  • 呼叫全域性的 beforeResolve 守衛 (2.5+)。

  • 導航被確認。

  • 呼叫全域性的 afterEach 勾點。

  • 觸發 DOM 更新。

  • 呼叫 beforeRouteEnter 守衛中傳給 next 的回撥函數,建立好的元件範例會作為回撥函數的引數傳入。

Vue.js的template編譯

簡而言之,就是先轉化成AST樹,再得到的render函數返回VNode(Vue的虛擬DOM節點),詳細步驟如下:

首先,通過compile編譯器把template編譯成AST語法樹(abstract syntax tree 即 原始碼的抽象語法結構的樹狀表現形式),compile是createCompiler的返回值,createCompiler是用以建立編譯器的。另外compile還負責合併option。

然後,AST會經過generate(將AST語法樹轉化成render funtion字串的過程)得到render函數,render的返回值是VNode,VNode是Vue的虛擬DOM節點,裡面有(標籤名、子節點、文字等等)

$nextTick 是什麼?

Vue 實現響應式並不是在資料發生後立即更新 DOM,使用 vm.$nextTick 是在下次 DOM 更新迴圈結束之後立即執行延遲迴撥。在修改資料之後使用,則可以在回撥中獲取更新後的 DOM

說說Vue的生命週期吧

什麼時候被呼叫?

  • beforeCreate :範例初始化之後,資料觀測之前呼叫
  • created:範例建立萬之後呼叫。範例完成:資料觀測、屬性和方法的運算、 watch/event 事件回撥。無 $el .
  • beforeMount:在掛載之前呼叫,相關 render 函數首次被呼叫
  • mounted:了被新建立的vm.$el替換,並掛載到範例上去之後呼叫改勾點。
  • beforeUpdate:資料更新前呼叫,發生在虛擬DOM重新渲染和打修補程式,在這之後會呼叫改勾點。
  • updated:由於資料更改導致的虛擬DOM重新渲染和打修補程式,在這之後會呼叫改勾點。
  • beforeDestroy:範例銷燬前呼叫,範例仍然可用。
  • destroyed:範例銷燬之後呼叫,呼叫後,Vue範例指示的所有東西都會解綁,所有事件監聽器和所有子範例都會被移除

每個生命週期內部可以做什麼?

  • created:範例已經建立完成,因為他是最早觸發的,所以可以進行一些資料、資源的請求。
  • mounted:範例已經掛載完成,可以進行一些DOM操作。
  • beforeUpdate:可以在這個勾點中進一步的更改狀態,不會觸發重渲染。
  • updated:可以執行依賴於DOM的操作,但是要避免更改狀態,可能會導致更新無線迴圈。
  • destroyed:可以執行一些優化操作,清空計時器,解除繫結事件。

ajax放在哪個生命週期?:一般放在 mounted 中,保證邏輯統一性,因為生命週期是同步執行的, ajax 是非同步執行的。單數伺服器端渲染 ssr 同一放在 created 中,因為伺服器端渲染不支援 mounted 方法。 什麼時候使用beforeDestroy?:當前頁面使用 $on ,需要解綁事件。清楚定時器。解除事件繫結, scroll mousemove

Vue 怎麼用 vm.$set() 解決物件新增屬性不能響應的問題 ?

受現代 JavaScript 的限制 ,Vue 無法檢測到物件屬性的新增或刪除。由於 Vue 會在初始化範例時對屬性執行 getter/setter 轉化,所以屬性必須在 data 物件上存在才能讓 Vue 將它轉換為響應式的。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 來實現為物件新增響應式屬性,那框架本身是如何實現的呢?

我們檢視對應的 Vue 原始碼:vue/src/core/instance/index.js

export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 為陣列  
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改陣列的長度, 避免索引>陣列長度導致splcie()執行有誤
    target.length = Math.max(target.length, key)
    // 利用陣列的splice變異方法觸發響應式  
    target.splice(key, 1, val)
    return val
  }
  // key 已經存在,直接修改屬性值  
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 本身就不是響應式資料, 直接賦值
  if (!ob) {
    target[key] = val
    return val
  }
  // 對屬性進行響應式處理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

我們閱讀以上原始碼可知,vm.$set 的實現原理是:

  • 如果目標是陣列,直接使用陣列的 splice 方法觸發相應式;
  • 如果目標是物件,會先判讀屬性是否存在、物件是否是響應式,最終如果要對屬性進行響應式處理,則是通過呼叫 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化物件時,給物件屬性採用 Object.defineProperty 動態新增 getter 和 setter 的功能所呼叫的方法)

(學習視訊分享:、)

以上就是2022年vue高頻面試題分享(附答案分析)的詳細內容,更多請關注TW511.COM其它相關文章!