溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue高頻面試題實例分析

發(fā)布時間:2022-08-02 09:18:47 來源:億速云 閱讀:100 作者:iii 欄目:編程語言

本文小編為大家詳細介紹“vue高頻面試題實例分析”,內容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“vue高頻面試題實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

vue高頻面試題實例分析

Vue-router 導航守衛(wèi)有哪些

  • 全局前置/鉤子:beforeEach、beforeResolve、afterEach

  • 路由獨享的守衛(wèi):beforeEnter

  • 組件內的守衛(wèi):beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

為什么在 Vue3.0 采用了 Proxy,拋棄了 Object.defineProperty?

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

由于只針對了以上 7 種方法進行了 hack 處理,所以其他數(shù)組的屬性也是檢測不到的,還是具有一定的局限性。

Object.defineProperty 只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷。Vue 2.x 里,是通過 遞歸 + 遍歷 data 對象來實現(xiàn)對數(shù)據(jù)的監(jiān)控的,如果屬性值也是對象那么需要深度遍歷,顯然如果能劫持一個完整的對象是才是更好的選擇。

Proxy 可以劫持整個對象,并返回一個新的對象。Proxy 不僅可以代理對象,還可以代理數(shù)組。還可以代理動態(tài)增加的屬性。

v-for 為什么要加 key

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

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

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

如何從真實DOM到虛擬DOM

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

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

  • 優(yōu)化樹

  • ast 樹生成代碼

為什么Vue采用異步渲染呢?

Vue 是組件級更新,如果不采用異步更新,那么每次更新數(shù)據(jù)都會對當前組件進行重新渲染,所以為了性能, Vue 會在本輪數(shù)據(jù)更新后,在異步更新視圖。核心思想 nextTick 。

dep.notify() 通知 watcher進行更新, subs[i].update 依次調用 watcher 的 update , queueWatcher 將watcher 去重放入隊列, nextTick( flushSchedulerQueue )在下一tick中刷新watcher隊列(異步)。

為什么vue組件中data必須是一個函數(shù)?

對象為引用類型,當復用組件時,由于數(shù)據(jù)對象都指向同一個data對象,當在一個組件中修改data時,其他重用的組件中的data會同時被修改;而使用返回對象的函數(shù),由于每次返回的都是一個新對象(Object的實例),引用地址不同,則不會出現(xiàn)這個問題。

MVC 和 MVVM 區(qū)別

MVC

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

  • Model(模型):是應用程序中用于處理應用程序數(shù)據(jù)邏輯的部分。通常模型對象負責在數(shù)據(jù)庫中存取數(shù)據(jù)

  • View(視圖):是應用程序中處理數(shù)據(jù)顯示的部分。通常視圖是依據(jù)模型數(shù)據(jù)創(chuàng)建的

  • Controller(控制器):是應用程序中處理用戶交互的部分。通??刂破髫撠煆囊晥D讀取數(shù)據(jù),控制用戶輸入,并向模型發(fā)送數(shù)據(jù)

MVC 的思想:一句話描述就是 Controller 負責將 Model 的數(shù)據(jù)用 View 顯示出來,換句話說就是在 Controller 里面把 Model 的數(shù)據(jù)賦值給 View。

MVVM

MVVM 新增了 VM 類

  • ViewModel 層:做了兩件事達到了數(shù)據(jù)的雙向綁定 一是將【模型】轉化成【視圖】,即將后端傳遞的數(shù)據(jù)轉化成所看到的頁面。實現(xiàn)的方式是:數(shù)據(jù)綁定。二是將【視圖】轉化成【模型】,即將所看到的頁面轉化成后端的數(shù)據(jù)。實現(xiàn)的方式是:DOM 事件監(jiān)聽。

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

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

注意:Vue 并沒有完全遵循 MVVM 的思想 這一點官網(wǎng)自己也有說明

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

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

Vue 為什么要用 vm.$set() 解決對象新增屬性不能響應的問題 ?你能說說如下代碼的實現(xiàn)原理么?

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

  • Vue使用了Object.defineProperty實現(xiàn)雙向數(shù)據(jù)綁定

  • 在初始化實例時對屬性執(zhí)行 getter/setter 轉化

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

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

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

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

我們閱讀以上源碼可知,vm.$set 的實現(xiàn)原理是:

  • 如果目標是數(shù)組,直接使用數(shù)組的 splice 方法觸發(fā)相應式;

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

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

defineReactive 方法就是 Vue 在初始化對象時,給對象屬性采用 Object.defineProperty 動態(tài)添加 getter 和 setter 的功能所調用的方法

Vue3.0 和 2.0 的響應式原理區(qū)別

Vue3.x 改用 Proxy 替代 Object.defineProperty。因為 Proxy 可以直接監(jiān)聽對象和數(shù)組的變化,并且有多達 13 種攔截方法。

相關代碼如下

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

export function reactive(target) {
  // 根據(jù)不同參數(shù)創(chuàng)建不同響應式對象
  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函數(shù)的過程。會經(jīng)歷以下階段:

  • 生成AST樹

  • 優(yōu)化

  • codegen

首先解析模版,生成AST語法樹(一種用JavaScript對象的形式來描述整個模板)。 使用大量的正則表達式對模板進行解析,遇到標簽、文本的時候都會執(zhí)行對應的鉤子進行相關處理。

Vue的數(shù)據(jù)是響應式的,但其實模板中并不是所有的數(shù)據(jù)都是響應式的。有一些數(shù)據(jù)首次渲染后就不會再變化,對應的DOM也不會變化。那么優(yōu)化過程就是深度遍歷AST樹,按照相關條件對樹節(jié)點進行標記。這些被標記的節(jié)點(靜態(tài)節(jié)點)我們就可以跳過對它們的比對,對運行時的模板起到很大的優(yōu)化作用。

編譯的最后一步是將優(yōu)化后的AST樹轉換為可執(zhí)行的代碼。

MVVM的優(yōu)缺點?

優(yōu)點:

  • 分離視圖(View)和模型(Model),降低代碼耦合,提?視圖或者邏輯的重?性: ?如視圖(View)可以獨?于Model變化和修改,?個ViewModel可以綁定不同的"View"上,當View變化的時候Model不可以不變,當Model變化的時候View也可以不變。你可以把?些視圖邏輯放在?個ViewModel??,讓很多view重?這段視圖邏輯

  • 提?可測試性: ViewModel的存在可以幫助開發(fā)者更好地編寫測試代碼

  • ?動更新dom: 利?雙向綁定,數(shù)據(jù)更新后視圖?動更新,讓開發(fā)者從繁瑣的?動dom中解放

缺點:

  • Bug很難被調試: 因為使?雙向綁定的模式,當你看到界?異常了,有可能是你View的代碼有Bug,也可能是Model的代碼有問題。數(shù)據(jù)綁定使得?個位置的Bug被快速傳遞到別的位置,要定位原始出問題的地?就變得不那么容易了。另外,數(shù)據(jù)綁定的聲明是指令式地寫在View的模版當中的,這些內容是沒辦法去打斷點debug的

  • ?個?的模塊中model也會很?,雖然使??便了也很容易保證了數(shù)據(jù)的?致性,當時?期持有,不釋放內存就造成了花費更多的內存

  • 對于?型的圖形應?程序,視圖狀態(tài)較多,ViewModel的構建和維護的成本都會?較?。

Vue data 中某一個屬性的值發(fā)生改變后,視圖會立即同步執(zhí)行重新渲染嗎?

不會立即同步執(zhí)行重新渲染。Vue 實現(xiàn)響應式并不是數(shù)據(jù)發(fā)生變化之后 DOM 立即變化,而是按一定的策略進行 DOM 的更新。Vue 在更新 DOM 時是異步執(zhí)行的。只要偵聽到數(shù)據(jù)變化, Vue 將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。

如果同一個watcher被多次觸發(fā),只會被推入到隊列中一次。這種在緩沖時去除重復數(shù)據(jù)對于避免不必要的計算和 DOM 操作是非常重要的。然后,在下一個的事件循環(huán)tick中,Vue 刷新隊列并執(zhí)行實際(已去重的)工作。

diff算法

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

理解:

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

    • 擴展 v-for 為什么要有 key ,沒有 key 會暴力復用,舉例子的話隨便說一個比如移動節(jié)點或者增加節(jié)點(修改DOM),加 key 只會移動減少操作DOM。

  • 只有是同一個虛擬節(jié)點才會進行精細化比較,否則就是暴力刪除舊的,插入新的。

  • 只進行同層比較,不會進行跨層比較。

diff算法的優(yōu)化策略:四種命中查找,四個指針

  1. 舊前與新前(先比開頭,后插入和刪除節(jié)點的這種情況)

  2. 舊后與新后(比結尾,前插入或刪除的情況)

  3. 舊前與新后(頭與尾比,此種發(fā)生了,涉及移動節(jié)點,那么新前指向的節(jié)點,移動到舊后之后)

  4. 舊后與新前(尾與頭比,此種發(fā)生了,涉及移動節(jié)點,那么新前指向的節(jié)點,移動到舊前之前)

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

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

Vue的優(yōu)點

  • 輕量級框架:只關注視圖層,是一個構建數(shù)據(jù)的視圖集合,大小只有幾十 kb

  • 簡單易學:國人開發(fā),中文文檔,不存在語言障礙 ,易于理解和學習;

  • 雙向數(shù)據(jù)綁定:保留了 angular 的特點,在數(shù)據(jù)操作方面更為簡單;

  • 組件化:保留了 react 的優(yōu)點,實現(xiàn)了 html 的封裝和重用,在構建單頁面應用方面有著獨特的優(yōu)勢;

  • 視圖,數(shù)據(jù),結構分離:使數(shù)據(jù)的更改更為簡單,不需要進行邏輯代碼的修改,只需要操作數(shù)據(jù)就能完成相關操作;

  • 虛擬DOM:dom 操作是非常耗費性能的,不再使用原生的 dom 操作節(jié)點,極大解放 dom 操作,但具體操作的還是 dom 不過是換了另一種方式;

  • 運行速度更快:相比較于 react 而言,同樣是操作虛擬 dom,就性能而言, vue 存在很大的優(yōu)勢。

vue-router 路由鉤子函數(shù)是什么 執(zhí)行順序是什么

路由鉤子的執(zhí)行流程, 鉤子函數(shù)種類有:全局守衛(wèi)、路由守衛(wèi)、組件守衛(wèi)

完整的導航解析流程:

  • 導航被觸發(fā)。

  • 在失活的組件里調用 beforeRouteLeave 守衛(wèi)。

  • 調用全局的 beforeEach 守衛(wèi)。

  • 在重用的組件里調用 beforeRouteUpdate 守衛(wèi) (2.2+)。

  • 在路由配置里調用 beforeEnter。

  • 解析異步路由組件。

  • 在被激活的組件里調用 beforeRouteEnter。

  • 調用全局的 beforeResolve 守衛(wèi) (2.5+)。

  • 導航被確認。

  • 調用全局的 afterEach 鉤子。

  • 觸發(fā) DOM 更新。

  • 調用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調函數(shù),創(chuàng)建好的組件實例會作為回調函數(shù)的參數(shù)傳入。

Vue.js的template編譯

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

首先,通過compile編譯器把template編譯成AST語法樹(abstract syntax tree 即 源代碼的抽象語法結構的樹狀表現(xiàn)形式),compile是createCompiler的返回值,createCompiler是用以創(chuàng)建編譯器的。另外compile還負責合并option。

然后,AST會經(jīng)過generate(將AST語法樹轉化成render funtion字符串的過程)得到render函數(shù),render的返回值是VNode,VNode是Vue的虛擬DOM節(jié)點,里面有(標簽名、子節(jié)點、文本等等)

$nextTick 是什么?

Vue 實現(xiàn)響應式并不是在數(shù)據(jù)發(fā)生后立即更新 DOM,使用 vm.$nextTick 是在下次 DOM 更新循環(huán)結束之后立即執(zhí)行延遲回調。在修改數(shù)據(jù)之后使用,則可以在回調中獲取更新后的 DOM。

說說Vue的生命周期吧

什么時候被調用?

  • beforeCreate :實例初始化之后,數(shù)據(jù)觀測之前調用

  • created:實例創(chuàng)建萬之后調用。實例完成:數(shù)據(jù)觀測、屬性和方法的運算、 watch/event 事件回調。無 $el .

  • beforeMount:在掛載之前調用,相關 render 函數(shù)首次被調用

  • mounted:了被新創(chuàng)建的vm.$el替換,并掛載到實例上去之后調用改鉤子。

  • beforeUpdate:數(shù)據(jù)更新前調用,發(fā)生在虛擬DOM重新渲染和打補丁,在這之后會調用改鉤子。

  • updated:由于數(shù)據(jù)更改導致的虛擬DOM重新渲染和打補丁,在這之后會調用改鉤子。

  • beforeDestroy:實例銷毀前調用,實例仍然可用。

  • destroyed:實例銷毀之后調用,調用后,Vue實例指示的所有東西都會解綁,所有事件監(jiān)聽器和所有子實例都會被移除

每個生命周期內部可以做什么?

  • created:實例已經(jīng)創(chuàng)建完成,因為他是最早觸發(fā)的,所以可以進行一些數(shù)據(jù)、資源的請求。

  • mounted:實例已經(jīng)掛載完成,可以進行一些DOM操作。

  • beforeUpdate:可以在這個鉤子中進一步的更改狀態(tài),不會觸發(fā)重渲染。

  • updated:可以執(zhí)行依賴于DOM的操作,但是要避免更改狀態(tài),可能會導致更新無線循環(huán)。

  • destroyed:可以執(zhí)行一些優(yōu)化操作,清空計時器,解除綁定事件。

ajax放在哪個生命周期?:一般放在 mounted 中,保證邏輯統(tǒng)一性,因為生命周期是同步執(zhí)行的, ajax 是異步執(zhí)行的。單數(shù)服務端渲染 ssr 同一放在 created 中,因為服務端渲染不支持 mounted 方法。 什么時候使用beforeDestroy?:當前頁面使用 $on ,需要解綁事件。清楚定時器。解除事件綁定, scroll mousemove 。

Vue 怎么用 vm.$set() 解決對象新增屬性不能響應的問題 ?

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

我們查看對應的 Vue 源碼:vue/src/core/instance/index.js

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

我們閱讀以上源碼可知,vm.$set 的實現(xiàn)原理是:

  • 如果目標是數(shù)組,直接使用數(shù)組的 splice 方法觸發(fā)相應式;

  • 如果目標是對象,會先判讀屬性是否存在、對象是否是響應式,最終如果要對屬性進行響應式處理,則是通過調用 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化對象時,給對象屬性采用 Object.defineProperty 動態(tài)添加 getter 和 setter 的功能所調用的方法)

讀到這里,這篇“vue高頻面試題實例分析”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

vue
AI