【整理彙總】45+個Vue面試題,帶你鞏固知識點!

2023-01-03 22:00:24
本篇文章給大家總結分享一些面試題(附答案解析),帶你梳理基礎知識,增強Vue知識儲備,值得收藏,快來看看吧!

1. 簡述 Vue 生命週期

答題思路:

  • Vue 生命週期是什麼?

  • Vue 生命週期有哪些階段?

  • Vue 生命週期的流程?

  • 結合實踐

  • 擴充套件:在 Vue3 變化 Vue 生命週期的變化 【相關推薦:、】

回答範例:

  • 生命週期這個詞應該是很好理解的,在我們生活中就會常常碰到,比如談到一個人的生命週期,我們會說人這一生會經歷嬰兒、兒童、少年、青年、中年、老年這幾個階段。 而 Vue 的生命週期也是如此,在 Vue 中的每個元件都會經歷從建立掛載更新再到銷燬這幾個階段,而在這些階段中,Vue 會執行一種叫做生命週期勾點的函數,方便我們在特定的階段有機會新增上我們自己的程式碼。

  • Vue 生命週期總共可以分為 8 各階段:建立前後掛載前後更新前後銷燬前後,以及一些特殊場景的生命週期(keep-alive 啟用時捕獲後代元件錯誤時)。Vue3 中還新增了三個用於偵錯和伺服器端渲染場景

  • 這幾個階段對應的勾點函數 API依次為:beforeCreate create beforeMount mounted beforeUpdate updated activated(keep-alive 啟用時呼叫) deactivated(keep-alive 停用時呼叫) beforeDestory destoryed errorCaptured(捕獲子孫元件錯誤時呼叫)

    在 Vue3 中的變化 絕大多數只要加上字首 on 即可,比如 mounted 變為 onMounted,除了 beforeDestroydestroyed 被重新命名為 beforeUnmountunMounted(這樣與前面的 beforeMountmounted 對應,強迫症表示很贊?)

  • beforeCreate 在元件建立前呼叫,通常用於外掛開發中執行一些初始化任務;created 元件建立完畢呼叫,可以存取各種資料,請求介面資料等;mounted 元件掛載時呼叫 可以存取資料、dom 元素、子元件等;beforeUpdate 更新前呼叫 此時 view 層還未更新,可用於獲取更新前的各種狀態;updated 完成更新時呼叫 此時view層已經完成更新,所有狀態已經是最新的了;beforeUnmount 範例被銷燬前呼叫,可用於一些定時器或訂閱的取消;unMounted 銷燬一個範例時呼叫 可以清理與其他範例的連結,解綁它的全部指令以及事件監聽器。

  • 在 Vue3 中: setup 是比 created 先執行的而且沒有 beforeCreatecreated

2. Vue 中如何做許可權管理

  • 許可權管理一般需求就是對頁面許可權和按鈕許可權的管理

  • 具體實現的時候分前端實現和後端實現兩種方案: 前端方案會把所有路由資訊在前端設定,通過路由守衛要求使用者登入,使用者登入後根據角色過濾出路由表,然後在動態新增路由。比如我會設定一個 asyncRoutes 陣列,需要認證的頁面在路由的 meta 中新增一個 roles 欄位,等獲取使用者角色之後取兩者的交集,若結果不為空則說明可以存取。過濾結束後剩下的路由就是使用者能存取的頁面,最後通過 router.addRoutes(accessRoutes) 方式動態新增路由即可。

    後端方案會把所有頁面路由資訊存在資料庫中,使用者登入的時候根據其角色查詢得到其能存取的所有路由資訊返回給前端,前端再通過 addRoute 動態新增路由資訊。

    按鈕許可權的控制通常會實現一個指令,例如 v-permission,將按鈕要求角色通過值傳給 v-permission 指令,在指令的 mounted 勾點中可以判斷當前使用者角色和按鈕是否存在交集,有就保留按鈕,沒有就移除按鈕。

  • 純前端方案的優點是實現簡單,不需要額外許可權管理頁面,但是維護起來問題比較大,有新的頁面和角色需求就要修改前端程式碼和重新打包部署伺服器端方案就不存在這個問題,通過專門的角色和許可權管理頁面,設定頁面和按鈕許可權資訊到資料庫,應用每次登陸時獲取的都是最新的路由資訊。

自己的話:許可權管理一般分頁面許可權和按鈕許可權,而具體實現方案又分前端實現和後端實現,前端實現就是會在前端維護一份動態的路由陣列,通過使用者登入後的角色來篩選它所擁有許可權的頁面,最後通過 addRoute 將動態新增到 router 中;而後端實現的不同點就是這些路由是後端返回給前端,前端再動態新增進去的。 按鈕許可權一般會實現一個 v-permission ,通過判斷使用者有沒有許可權來控制按鈕是否顯示。 純前端方案的優點是實現簡單,但是維護問題大,有新的頁面和角色需求都需要改程式碼重新打包部署,伺服器端則不存在這個問題。

3. Vue 中雙向繫結的使用和原理

回答思路:

  • 什麼是雙向繫結?

  • 雙向繫結的好處?

  • 在什麼地方使用雙向繫結?

  • 雙向繫結的使用方式、使用細節、Vue3中的變化

  • 原理實現描述

回答:

  • Vue中的雙向繫結是一個指令 v-model ,它可以繫結一個響應式資料到檢視,同時檢視中變化也能改變該值。

  • v-model 是一個語法糖,它的原理(預設請情況下)就是通過 :value 將變數掛到 dom 上,再通過 input事件 監聽 dom 的變化改變變數的值。使用 v-model的好處就是方便呀,減少了大量的繁瑣的事件處理,提高開發效率。

  • 通常在表單上使用 v-model,還可以在自定義元件上使用,表示某個值得輸入和輸出控制。

  • 可以結合修飾符做進一步限定(lazy/number/trim),用在自定義元件上時有些不同,它相當於是給了子元件一個 modelValue 的 屬性 和 update:modelValue 的 事件; 在 Vue3 中還可以用引數形式指定多個不同的繫結,如 v-model:foo 這個時候就相當於 給了子元件一個 foo 的 屬性 和 update:foo 的事件。

  • v-model作為一個指令,它的原理就是 Vue 編譯器會把它轉換成 value屬性繫結和input的監聽事件,上面說過是預設情況下,實際上編譯器會根據表單元素的不同分配不同的事件,比如 checkboxradio 型別的input 會轉換為 checkedchange 事件。

4. Vue 元件之間通訊有哪些?

Vue 元件之間通訊有以下這麼幾種:

  • props

  • $emit$on$off$once(後三者在Vue3中已被廢除)

  • $children(Vue3中廢除)$parent

  • $attrs$listeners(Vue3中廢除)

  • ref

  • $root

  • eventbus (Vue3中不好使了,需要自己封裝)

  • vuexpinia

  • provide + inject

以上的方法長按使用場景可以分為:

  • 父子元件之間可以使用

    props /$emit/ $parent/ ref /$attrs

  • 兄弟元件之間可以使用

    $parent / $root/ eventbus / vuex

  • 跨層及元件之間可以使用

    eventbus / vuex pinia / provide + inject

5.你瞭解哪些 Vue 效能優化方法?

  • 路由懶載入:有效拆分 App 尺寸,存取時才非同步載入

const router = createRouter({
    routes: [
        { path : '/foo', component: () => import('./foo.vue)}
    ]
})
登入後複製
  • keep-alive 快取頁面:避免重複建立元件範例,且能儲存快取元件狀態

<keep-alive>
  <router-view v-if="$route.meta.keepAlive == true"></router-view>
</keep-alive>
<router-view v-if="$route.meta.keepAlive != true"></router-view>
登入後複製
  • 使用 v-show 複用 DOM:避免重複建立元件

  • v-for 遍歷避免同時使用 v-if(實際上這在 Vue3 中是錯誤的寫法)

  • v-oncev-memo: 不再變化的資料使用 v-once;按條件跳過更新時使用 v-memo

  • 長列表效能優化:如果是巨量資料長列表,可採用虛擬捲動,只渲染少部分割區域的內容。一些開源庫(vue-virtual-scroller / vue-virtual-scroll-grid

  • 事件的銷燬:Vue元件銷燬時,會自動解綁它的全部指令以及事件監聽器,但是僅限於元件本身的事件。

  • 圖片懶載入,自定義 v-lazy 指令 (參考專案:vue-lazyload

  • 第三方外掛按需引入 element-plus 避免體積太大

  • 子元件分割策略:較重的狀態元件適合拆分

  • SSR 伺服器端渲染 解決首屏渲染慢的問題

6. 重新整理後 Vuex 狀態丟失怎麼解決?

思路:

  • 重新整理後 Vuex 狀態為什麼會丟失?

  • 解決方法

  • 第三方庫以及原理探討

  • 個人理解

回答:

  • 因為 Vuex 只是在記憶體中儲存狀態,重新整理後就會丟失,如果要持久化就要存起來。

  • 可以是用 localStorage 儲存 Vuex 的狀態,store 中把值取出來作為 state 的初始值,提交 mutation 的時候就存入 localStorage

  • 可以用 vuex-persistvuex-persistedstate 這種外掛,可以通過外掛選項控制哪些需要持久化。內部的原理就是通過訂閱 mutation 變化做統一處理。

  • 這裡有兩個問題,一是如果使用者手動改了 localStorage怎麼辦?那我 Vuex 裡的狀態不是也改變了?二是由於 localStorage API 的原因只能儲存字串,所以我們只能將資料通過 JSON.stringify 轉換為字串,而當我們儲存的資料為 MapSetFunction 這種參照型別的資料時,JSON.stringify 轉換後會變味 {} 而丟失。

對應第一個問題我的解決方法是可以通過 監聽 storage 事件來清除資料

window.addEventListener("storage", function () {
    localStorage.clear();
    window.location.href = '/login'
    console.error("不要修改localStorage的值~~~");
});
登入後複製

對於第二個問題沒辦法了,只能選擇不適用 MapSet 這種參照型別。

7. Vue3 為什麼用 Proxy 替代 defineProperty ?

思路:

  • 屬性攔截的幾種方式

  • defineProperty的問題

  • Proxy的優點

  • 其他考量

回答:

  • JS 中做屬性攔截常見的方式有三種:definePropertygetter/settersProxy

  • Vue2 中使用 defineProperty 的原因是, 2013 年只能使用這種方式,由於該 API 存在一些侷限性,比如對於陣列的攔截有問題,為此 Vue 需要專門為陣列響應式做一套實現。另外不能攔截那些新增、刪除屬性;最後 defineProperty 方案在初始化時需要深度遞迴遍歷處理物件才能對它進行完全攔截,明顯增加了初始化的時間。

  • 以上兩點在 Proxy 出現後迎刃而解,不僅可以對陣列實現攔截,還能對 MapSet 實現攔截;另外 Proxy 的攔截也是懶處理行為,如果使用者沒有存取巢狀物件,那麼也不會實施攔截,這就讓初始化的速度和記憶體佔用改善了。

  • Proxy 有相容性問題,完全不支援IE

8. 怎麼實現路由懶載入?

思路:

  • 必要性

  • 何時用

  • 怎麼用

  • 使用細節

回答:

  • 當打包構建時,Javascript 抱回變得非常大,影響頁面載入。利用路由懶載入我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被存取的時候才載入對應最賤,這樣更加高效,是一種優化手段。

  • 一般來說,對於所有的路由都使用動態匯入是個好主意

  • component 選項設定一個返回 Promise元件的函數就可以定義懶載入路由.例如:

{
  path: '/login',
  component: () => import('../views/login/Login.vue')
},
登入後複製
  • 結合註釋

{
  path: '/login',
  component: () => import(/* webpackChunkName: "login" */'../views/login/Login.vue')
},
登入後複製

vite中結合rollupOptions定義分塊 5. 路由中不能使用非同步元件

9. history模式 和 hash 模式有何區別?

  • Vue-Router 有三個模式,其中 history 和 hash 更為常用。兩者差別主要在顯示形式和部署上,

  • hash模式在位址列現實的時候有一個 #,這種方式使用和部署都較簡單;history模式url看起來更優雅沒關,但是應用在部署時需要做特殊設定,web伺服器需要做回退處理,否則會出現重新整理頁面404的問題。

  • 在實現上 hash模式是監聽hashchange事件觸發路由跳轉,history模式是監聽popstate 事件觸發路由跳轉。

10. 說說 nextTick 的使用和原理?

  • VuenextTick 是等待下一次 DOM 更新重新整理的工具方法。

  • Vue 有一個非同步更新策略,意思是如果資料變化,Vue 不會立刻更新 DOM,而是開啟一個佇列,把元件更新函數儲存在佇列中,在同一時間迴圈中發生的所有資料變更會非同步的批次更新。這一策略導致我們對資料的修改不會立刻體現在 DOM 上,此時如果想要獲取更新後的 DOM 狀態,就需要使用 nextTicknextTick 接受一個函數,我們可以在這個函數內部存取最新的 DOM 狀態 在開發時,有兩個場景我們會用到 nextTick

    • created 中想要獲取 DOM;
    • 響應式資料變化後獲取 DOM 更新後的狀態;
  • nextTick 的原理:在 Vue 內部,nextTick 之所以能夠讓我們看到 DOM 更新後的結果,是因為我們傳入的 callback 會被新增到佇列重新整理函數的後面,這樣等佇列內部的更新函數都執行完畢,所有 DOM 操作也就結束了,callback 自然能夠獲取最新的 DOM 值。

11. v-for 和 v-if 優先順序

先回答答案:vue2 中, v-for 的優先順序更高 但是在 vue3 中, v-if 的優先順序更高

拓展:無論什麼時候,我們都不應該把 v-forv-if 放在一起, 怎麼解決呢?一是可以定義一個計算屬性,讓 v-for 遍歷計算屬性。二是可以把 if 移到內部容器裡(ul ol)或者把v-for移植外部容器(template)中

12. 如何監聽 Vuex 狀態變化?

  • watch

  • store.subscribe()

watch 方式,可以以字串形式監聽 $store.state.xx; subscribe 方法引數是一個回撥函數,回撥函數接受mutation 物件和 state 物件,可以通過 mutation.type 判斷監聽的目標。 wtach 方法更簡單好用, subscribe 會略繁瑣,一般用 vuex 外掛中(可以提一下vuex的持久化外掛vuex-persistvuex-persistedstate

13. 你覺得 Vuex 有什麼缺點?

  • 不支援持久化,頁面重新整理狀態就會丟失

  • 使用模組比較繁瑣

  • 不支援 ts (或者說很不友好)

vue3 + pinia 會是更好的組合。

14. ref 和 reactive 異同點?

  • 兩者都能返回響應式物件,ref 返回的是一個響應式Ref 物件, reactive 返回的是響應式代理物件。

  • ref 通常是處理單值得響應式,reactive 用於處理物件型別的資料響應式

  • ref 需要通過 .value 存取, 在檢視中會自動脫 ref,不需要 .valueref 可以接收物件或陣列但內部依然是 reactive 實現的;reactive 如果接收 Ref 物件會自動脫 ref ;使用展開運運算元展開 reactive 返回的響應式物件會使其失去響應性,可以結合 toRefs()將值轉換為 Ref 物件後再展開。

  • reactive 內部使用 Prxoy 代理攔截物件各種操作,而 ref 內部封裝一個 RefImpl 類,設定 get value/set value,攔截使用者對值得存取。

16. Vue 中如何擴充套件一個元件?

  • 邏輯擴充套件:mixinsextendscomposition api:

  • 內容擴充套件:slots

mixins 很靈活,但是會衝突很混亂。extends 是一個不太常用的選項,更 mixins 的不同是它只能擴充套件單個物件,優先順序比 mixins 高。

混入的資料和方法 不能明確判斷來源 而且可能和當前元件內變數 產生命名衝突,composition api 可以很好解決這些問題,利用獨立出來的響應式模組可以很方便的編寫獨立邏輯並提供響應式資料局,增強程式碼的可讀性和維護性。

擴充套件:Vue.mixin(全域性混入) Vue.extend(有點像是 類/元件的繼承 建立一個子類)

17. vue-loader 是什麼?

  • vue-loader 是用於處理單檔案元件(SFC)的webpack loader

  • 因為有了 vue-loader,我們才能用 .vue 檔案形式編寫程式碼,將程式碼分割為 template script style

  • webpack 在打包的時候,會以 loader 的方式呼叫 vue-loader

  • vue-loader 被執行時,它會對 SFC 中的每個語言塊用單獨的 loader 鏈處理,最後將這些單獨的塊裝配成最終的元件模組

18. 子元件能否修改父元件資料

不能直接改。

元件化開發中有一個單向資料流原則,不在子元件修改父元件資料是個常識

如果你確實需要改,請通過emit向父元件傳送一個事件,在父元件中修改

19. 怎麼定義動態路由,怎麼獲取傳過來的動態引數?

我麼可以在路徑中使用一個動態欄位來實現,例如/users/:id 其中 :id 就是路徑引數。 可以通過 this.$route.parmas獲取,引數還可以有多個, $route 物件還公開了其他有用的資訊如 query hash等。

20. 說說對 Vue 資料響應式的理解

思路:

  • 什麼是響應式?

  • 為什麼vue需要響應式?

  • 有什麼好處?

  • vue的響應式怎麼實現的,有哪些優缺點?

  • vue3中的響應式的新變化

回答:

  • 資料響應式就是 能夠監測到資料變化並且做出響應的一種機制

  • vue 中要解決的一個核心問題就是連線資料層和檢視層,通過資料變化驅動檢視更新,要做到這點就需要對資料做響應式處理。

  • 通過資料響應式加上虛擬 DOMpatch 演演算法,我們只需要運算元據,關心業務,完全不需要接觸繁瑣的 DOM 操作,打打提升了開發效率,降低開發難度。

  • vue2 中實現資料響應式的核心就是通過 Object.defineProperty() 方法對資料進行攔截,當 get 資料時做依賴收集 set 資料時做更新通知。這種機制很好的及絕了資料響應式的問題,但是實際使用也存在缺點,比如在初始化時的遞迴遍歷會造成效能損失;無法監聽新增或刪除屬性,在 vue 中要通過像 Vue.set/delete 這種特定的 API 才能實現對物件陣列屬性的新增和刪除,而且也不支援 MaSet這些資料結構,

  • 為了解決這些問題,Vue3 重寫了這部分實現,利用的是 ES6 中的 Proxy 代理要響應化的資料。它有很多好處,初始化效能和記憶體都大幅改善,也不需要特殊的 API ,但是不支援 IE 瀏覽器。

21. 從 template 到 render 做了什麼

templaterender 的過程其實是問的 vue 編譯器 工作原理。

思路:

  • 引入編譯器概念

  • 說明編譯器的必要性

  • 闡述編譯器工作流程

回答:

  • Vue 中有個獨特的編譯模組,稱為 compiler,它的主要作用是將 template 編譯為 js 可執行的 render 函數

  • 之所以需要這個編譯過程是為了便於我們高校的編寫試圖模版。相比而言,我們還是更願意用 HTML來編寫檢視,直觀且高效。手寫 render 函數不僅效率低下,而且失去了被編譯器的優化能力。

  • Vue 編譯器 首先會對 template進行解析Parse ),結束後會得到一個抽象語法樹AST,然後對 AST 進行深加工轉換(transform),最後將得到的 AST 生成為 js 程式碼,也就是 render 函數

22. 如何快取和更新元件

  • 快取元件可以使用 keep-alive 元件,include 和 exclude 可以指定包含不包含哪些元件。

  • Vue3 結合 vue-router 使用變化非常大,之前是 keep-alive 包含 router-view,現在是 router-view 包含 keep-alive

  • 快取後如果想要獲取資料可以使用 actived 勾點 或者 beforeRouteEntervue-router 的一個守衛)

  • keep-alive 是一個通用元件,它內部定義了一個 map,快取建立過的元件範例,它返回的渲染函數內部會查詢內嵌的 component 元件對應元件的 vnode,如果改元件在map中存在就直接返回它。由於 componentis 屬性是一個響應式資料,因此只要它變化,keep-aliverender 函數就會重新執行。

23. 虛擬DOM

  • 虛擬 DOM 是什麼? 虛擬 DOM 的本質就是一個 Javascript 物件。

  • 為什麼要引入虛擬 DOM?(好處) 它能有效減少操作 DOM 的次數,方便實現跨平臺

  • 虛擬DOM如何生成?compiler 編譯器會把 template 模版編譯成渲染函數,接下來在 mount 掛載的過程會呼叫這個渲染函數,返回的物件就是 虛擬DOM 。掛載結束後,會進入更新流程。如果某些響應式資料發生變化,將會引起元件重新 render,此時會生成新的 虛擬DOM,和上次渲染結果做 diff 操作,最小量的操作 dom,從而高效更新檢視。

24. 什麼是非同步元件

  • 非同步元件就是不會立即載入而是會在需要的時候載入的元件。在大型應用中,我們需要分割程式碼為更小的塊試就可以用非同步元件。

  • 不僅可以在路由切換時懶載入元件,還可以在元件中使用非同步元件,從而更細的分割程式碼。

  • 使用非同步元件最簡單的方式是直接給 defineAsyncComponet 指定一個 loader 函數,結合 ES 模組 動態匯入函數 import 可以快速實現。Vue3 還可以結合 Suspense 元件使用非同步元件。

  • 非同步元件容易和路由懶載入混淆,實際上不是一個東西。非同步元件不能被用於定義懶載入路由上,處理它的是 Vue 框架,處理路由元件載入的是 vue-router。但是可以在懶載入的路由元件中使用非同步元件。

25. 說說Vue長列表優化思路

  • 避免巨量資料量:可以採用分頁的方式獲取
  • 避免渲染大量資料:vue-virtual-scroller等虛擬捲動方案,只渲染視口範圍內的資料
  • 避免更新:可以使用v-once方式只渲染一次
  • 優化更新:通過v-memo快取組數,有條件更新,提高服用,避免不必要更新
  • 按需載入資料:可以採用 懶載入 方式,在使用者需要的時候在載入資料。

26. computed & watch

  • computed 是計算屬性,watch 是偵聽器。

  • computed 通常用於處理模版中複雜的邏輯,而 watch 通常用於需要監聽一個響應式物件的變化而做一些操作的時候

  • watch 可以進行非同步操作,computed 不行。

  • 計算屬性傳遞一個物件 有 setget 兩個選項,是它稱為即可讀又可寫的計算屬性,如果傳遞的是函數的話預設就是 get 選項,watch 可以傳遞一個物件,設定deep、immediate等選項

  • vue3watch 發生了一些變化,例如不能再偵測一個點操符之外的字串表示式,reactivity API 中新出的 watchwatchEffect 可以完全替代 watch 選項,而且功能更加強大

27. SPA 和 SSR的區別是什麼?

  • SPA(Single Page Application)是單頁面應用。一般也稱為使用者端渲染,簡稱 CSR 。SSR(Server Side Render) 即伺服器端渲染。一般也稱為多頁面應用(Mulpile Page Application),簡稱 MPA。

  • SPA 只會首次請求 html 檔案,後續只需要請求 JSON 資料即可,因此使用者體驗更好,節約流量,伺服器端壓力也較小。但是首屏載入的時間會變長,而且 SEO 不友好。為了解決以上缺點,就有了 SSR 方案,由於 HTML 內容在伺服器一次性生成出來,首屏載入快,搜尋引擎也可以很方便的抓取頁面資訊。但同時 SSR 方案也會有效能,開發受限等問題。

  • 選擇上,如果有首屏載入優化需求,SEO需求時,就可以考慮SSR。

  • 但並不是只有這一種替代方案,比如對一些不常變化的靜態網站,SSR反而浪費資源,我們可以考慮預渲染的方案。另外 nuxt.js/next.js 中給我們提供了SSG靜態網站生成方案也是很好的靜態站點解決方案,結合一些CI手段,可以起到很好的優化效果。

28. diff 演演算法

回答思路:

  • diff演演算法是幹什麼的?

  • 必要性

  • 何時執行

  • 具體執行方式

  • 拔高:說一下vue3中的優化

回答:

  • Vue 中的 diff 演演算法稱為 patching 演演算法,虛擬DOM要想轉化為真實DOM就需要通過 patch 方法轉換。

  • 最初 Vue1.x 檢視中農每個依賴均有更新函數對應,可以做到精確更新,因此不需要 虛擬DOMpatching 演演算法支援,但是這樣粒度過細導致 Vue1.x 無法承載較大應用;Vue2.x 中為了降低 Watcher 粒度,每個元件只有一個 Watcher 與之對應,此時就需要引入 patching 演演算法才能精確找到發生變化的地方並高效更新。

  • vuediff 執行的時刻是元件內響應式資料變更觸發範例執行其更新函數時,更新函數會再次執行 render函數 獲得最新的 虛擬DOM ,然後執行 patch函數對比新舊虛擬DOM,將其轉化為對應的 DOM 操作。

  • patch 過程是一個遞迴過程,遵循深度優先、同層比較的策略;以 vue3patch 為例:

    • 首先判斷兩個節點是否為相同同類節點,不同則刪除重新建立
    • 如果雙方都是文字則更新文字內容
    • 如果雙方都是元素節點則遞迴更新子元素,同時更新元素屬性
    • 更新子節點時又分了幾種情況:
      • 新的子節點是文字,老的子節點是陣列則清空,並設定文字;
      • 新的子節點是文字,老的子節點是文字則直接更新文字;
      • 新的子節點是陣列,老的子節點是文字則清空文字,並建立新子節點陣列中的子元素;
      • 新的子節點是陣列,老的子節點也是陣列,那麼比較兩組子節點,更新細節blabla
  • vue3 中引入的更新策略:編譯期優化 patchFlagsblock

29. 如何從0到1架構一個Vue專案,說說有哪些步驟,外掛,目錄結構怎麼組織

  • 從 0 建立專案我大致會做以下事情:專案構建、引入必要外掛、程式碼規範、提交規範、常用庫和元件

  • 目前vue3專案我會用vite或者create-vue建立專案

  • 接下來引入必要外掛:vue-router、vuex/pinia、element-plus、antd-vue、axios等等

  • 其他常用的庫有 像lodash、dayjs、nprogress等等..

  • 下面是程式碼規範: editorconfig、prettier、eslint

  • 最後是提交規範,可以使用husky、Commitizen

  • 目錄結構我喜歡按照下面的結構來

+ |- /src
+   |- /assets 存放資源
+     |- /img   
+     |- /css   
+     |- /font   
+     |- /data   
+   |- base-ui  存放多個專案中都會用到的公共元件
+   |- components 存放這個專案用到的公共元件
+   |- hooks 存放自定義hook
+   |- views 檢視
+   |- store 狀態管理
+   |- router 路由
+   |- service 網路請求
+   |- utils 工具
+   |- global 全域性註冊、全域性常數..
登入後複製

30. 你如何實現一個Vue-Router

一個 SPA 應用的路由需要解決的問題時頁面跳轉內容改變同時不重新整理,同時路由還需要已外掛形式存在,所以:

  • 首先我會定義一個 createRouter 函數,返回路由器範例,範例內部做幾件事;

    • 儲存使用者傳入的設定項
    • 監聽 hash 或者 popstate 事件
    • 回撥里根據 path 匹配對應路由

  • router 定義成一個 Vue 外掛,即實現 install 方法,內部做兩件事:

    • 實現兩個全域性元件:router-linkrouter-view,分別實現頁面跳轉和內容顯示
    • 定義兩個全域性變數:$router$route,元件內可以存取當前路由和路由器範例

31. 什麼情況需要使用Vuex模組?

  • 在專案規模變大的之後,單獨一個store物件會過於龐大臃腫,此時通過模組方式可以拆分來便於維護

  • 可以按之前規則單獨編寫資規模程式碼,然後在主檔案中通過 modules 選項組織起來:createStore({modules: {...}})

  • 使用時需要注意存取子模組狀態時需要加上註冊模組名。但同時gettersmutationsactions又在全域性空間中,使用方式和之前一樣。如果要做到完全拆分,需要在子模組加上 namespace選項,此時再存取它們就要加上名稱空間字首。

  • 模組的方式可以拆分程式碼,但是缺點也很明顯,使用起來比較繁瑣,容易出錯,而且型別系統支援很差,不能給我們帶來幫助。pinia 顯然在這方面有了很大改進,是時候切換過去了。

32. vue 元件為什麼只能有1個根節點

  • vue2 中元件確實只能有一個跟,但 vue3 中元件已經可以多根元件了。

  • 之所以需要這樣是因為 vdom 是一顆單根樹形結構,patch 方法在遍歷的時候從根節點開始遍歷,它要求只有一個根節點。元件也會轉換為一個 vdom,自然應該滿足這個要求。

  • vue3 中之所以可以寫多個根節點,是因為引入了 Fragment 的概念,這是一個抽象的節點,如果發現元件時多根的,就建立一個 Fragment 節點,把多個根節點作為它的 children。將來 pathch 的時候,如果發現是一個 Fragment 節點,則直接遍歷 children 建立或更新。

33. v-once 使用場景有哪些?

  • v-oncevue 的內建指令,作用是僅渲染指定元件或元素一次,並跳過未來對其更新。

  • 如果我們有一些元素或者元件再初始化渲染之後不再需要變化,這種情況下適合使用 v-once,這樣哪怕這些資料變化,vue 也會跳過更新,是一種程式碼優化手段。

  • 我們只需要作用的元件或元素上加上 v-once 即可。

補充:

  • vue3.2 之後,又增加了 v-memo,這個指令可以有條件的快取模板並控制他們的更新。

  • v-once 的原理:編譯器發現有 v-once 時,會將首次計算結果存入快取物件,元件再次渲染時就會從快取獲取,從而避免再次計算。

34. 什麼場景使用巢狀路由

  • 在平時開發中,應用的有些介面是由多層巢狀的元件組合而來的,這種情況下,url 各部分通常對應某個巢狀的元件,vue-router 中可以使用巢狀路由表示這種關係。
  • 表現形式是在兩個路由間切換時,他們有公用的檢視內容。此時通常提取一個父元件,內部放上 view-router,從而形成物理上的巢狀,和邏輯上的巢狀對應起來。定義巢狀路由時使用 children 屬性組織巢狀關係
  • 原理上是在 router-view 元件內部判斷其所處巢狀的深度,將這個深度作為匹配元件陣列 matched 的索引,獲取對應渲染元件並渲染之。

如果你說不出來,可以直接舉例子。當我開發一個頁面時,如果需要顯示一個頂部導航欄,通過導航欄跳轉到不同的頁面,而頂部的導航欄又必須要在每個頁面顯示時,就可以使用巢狀路由;還可以舉例,當我需要檢視某個列表的詳情頁面時,往往需要巢狀路由 (detail/:id

35. 如何監聽 Vuex 狀態變化?

  • watch

  • store.subscribe()

watch 方式,可以以字串形式監聽 $store.state.xx; subscribe 方法引數是一個回撥函數,回撥函數接受mutation 物件和 state 物件,可以通過 mutation.type 判斷監聽的目標。 wtach 方法更簡單好用, subscribe 會略繁瑣,一般

36. Vue 範例掛載過程發生了什麼?

  • 掛載範例的過程就是 app.mount()的過程,整體上就做了兩件事:初始化建立更新機制

  • 初始化會建立元件範例初始化元件狀態建立各種響應式資料

  • 簡歷更新機制這一步會立即執行一次元件更新函數,這會首次執行渲染函數並執行 patch 將前面獲得vnode 轉換為 dom;同時會建立它內部響應式資料和元件更新函數之間的依賴關係,這使得以後資料變化時會執行對應的更新函數。

37. key 的作用

  • key 的作用主要是為了更高效的更新虛擬 DOM

  • keyvuepatch 過程中判斷兩個節點是否是相同節點的關鍵條件(另一個是元素型別),如果不設定 key,它的值就是 undefinedvue 則可能永遠認為這是兩個相同節點,只能去做更新操作,這造成了大量的 dom 更新操作,明顯是不可取的。

  • 實際使用的過程中必須設定 key,而且應該儘量避免使用陣列索引,這可能導致一些隱藏 bug

38. watch 和 watchEffect

  • watchEffect立即執行函數,被動地追蹤它的依賴,傳入的函數即是依賴收集的資料來源,也是回撥函數;watch 偵測一個或多個響應式資料來源,在資料來源變化時呼叫一個回撥函數,通過 immediate 選項也可以設定立即執行一次。

  • watchEffect是一種特殊的 watch。如果不關心響應式資料前後的值,可以使用 watchEffect。其他情況都可以用 watch

39. 父子元件建立、掛載順序

parent created -> child created -> child mounted -> parent mounted

原因:Vue 建立是一個遞迴的過程,先建立父元件有子元件就會建立子元件,因此建立時先有父元件再有子元件;子元件首次建立時會新增 Mounted 勾點到佇列,等到 patch 結束再執行它們,可見子元件的 mounted 勾點是選進入到佇列中的,因此等到 patch 結束執行這些勾點時也先執行。

40. 說說你對 Vuex 的理解

  • vuex是一個專門為vue應用開發的狀態管理模式庫,

  • 當你遇到多個元件共用狀態時或者專案中的元件難以管理的時候就可以使用vuex,它以一個全域性單例模式管理全域性的狀態。

  • 基本核心概念有 state、mutation、action、getters、module等

  • 說些使用過程的感受 ts不友好 模組使用繁瑣 頁面重新整理資料也會消失

41. 什麼是遞迴元件?使用場景有哪些?

  • 如果某個元件通過元件名稱參照它自己,這種情況就是遞迴元件。

  • 類似 TreeMenu 這類元件,它們的節點往往包含子節點,子節點結構和父節點往往是相同的。這類元件的資料往往也是樹形結構,這種都是使用遞迴元件的典型場景。

42. 你寫過自定義指令嗎?

使用自定義指令分為定義、註冊、和使用

  • 定義有兩種方式,物件和函數形式,前者類似元件定義,有各種生命週期;後者只會在 mountedupdated 時執行

  • 註冊:可以使用 app.directive 全域性註冊 也可以通過選項區域性註冊

  • 使用時在註冊名稱前加上 v-即可。

  • v-copy 複製貼上

  • v-lazy 圖片懶載入

  • v-debounce 防抖

  • v-permission 按鈕許可權

  • v-longpress 長按

43. Vue3新特性

API 層面

  • Composition API

  • setup 語法糖

  • Teleport 傳送門

  • Fragments 可以多個根節點

  • Emits

  • createRenderer 自定義渲染器

  • SFC 狀態驅動 css 變數 (v-bind in <style>)

此外,Vue3在框架層面也有很多兩點和改進

  • 更快
    • 虛擬 DOM 重寫
    • 編譯器優化:靜態提升、patchFlagsblock
    • 基於 Proxy 的響應式系統
  • 更小:更好的搖樹優化
  • 更容易維護:TS + 模組化
  • 更容易擴充套件
    • 獨立的響應化模組
    • 自定義渲染器

44. Vue3設計目標和優化點

最大設計目標就是替代 Vue2,為了實現這一點,Vue3 在以下幾個方面做了很大改進,如:易用性,框架效能、擴充套件性、可維護性、開發體驗等

  • 易用性方面:主要有 API 簡化 v-model 變成了 v-modelsync 修飾符的結合體。類似的還有 h(type,props,children) 函數中的 props 不用考慮區分屬性、特性、事件等,框架替我們判斷,易用性增。

  • 開發體驗方面:新組建 Teleport Fragment Suspense 等都會簡化特定場景的程式碼編寫。 setup 語法糖更是極大提升了我們的開發體驗。

  • 擴充套件性方面提升: 如獨獨立的 reactivity 模組,custom render API

  • 可維護性方面主要是 Composition API,更容易編寫高複用性的業務邏輯。還有對TS支援的提升。

  • 效能方面:編譯器優化、基於 Proxy 的響應式系統。

  • 。。。

45. Vue3效能提升體現在哪些方面?

  • 程式碼方面:全新的響應式API,基於 Proxy 實現,初始化事件和記憶體佔用均大幅改進;

  • 編譯方面:做了更多編譯優化處理,比如靜態提升、動態內容標記、事件快取、區塊等,可以有效跳過大量diff過程

  • 打包方面:更好的支援 tree-shaking ,因此體積更小,載入更快.(因為vue3 所有的API都通過ES6模組化的方式引入,這樣就能讓webpack或rollup等打包工具在打包時對沒有用到API進行剔除,最小化bundle體積

46. $attrs$listeners 是做什麼的?

$attrs 獲取沒有在 props 中定義的屬性,v-bind="$attrs" 可以用於屬性透傳$listeners 用於獲取事件,vue3 中已經移除合併到 attrs 中,使用起來更方便

47. Composition API 和 Option API 有何不同?

Composition API 是一組API,包括 Reactivity API、生命勾點、依賴注入,使使用者可以通過匯入函數方式編寫元件,而 Options API 則通過宣告元件選項的物件形式編寫元件。

Composition API 更簡潔、邏輯複用更高效。解決的過去 Options APImixins 的各種缺點(會衝突很混亂);另外 Composition API 更自由,沒有 Options API 那樣固定的寫法,並且可以更有效的將邏輯程式碼組織在一起,而不用東一塊西一塊搞得很混亂,最後 Composition API 擁有更好的型別推斷,對 ts 支援友好。

48. 你知道哪些 Vue 最佳實踐

編碼風格方面:

  • 元件命名時使用 多詞風格避免和html元素衝突

  • 屬性名峰命名,模板或jsx中使用 肉串命名

  • v-for 務必加上key 且不要和v-if寫在一起‘’

效能方面:

  • 路由懶載入減少應用尺寸

  • SSR 減少首屏載入事件

  • v-once v-memo

  • 長列表 虛擬捲動技術

  • 對於深層巢狀物件的巨量資料可以使用 shallowRefshallowReactive 降低開銷

  • 避免不必要的元件抽象

49. mutation 和 action 的區別?

mutation 用於修改 stateaction 用於提交一個 mutation,而且 action 可以包含非同步操作

50. 如何從0實現vuex

  • 要實現一個 Store 儲存全域性狀態

  • 要提供修改狀態所需的API:commit({type, payload}), dispatch(type,payload)

實現 Store,可以定義 Store 類,建構函式接受選項 options,設定屬性 state 對外暴露狀態,提供 commitdispatch 修改屬性。這裡需要設定 state 為響應式物件,同時將 Store 定義為一個 Vue 外掛(install方法)。

commit 可以獲取使用者傳入 mutations 並執行它,這樣可以按使用者提供的方法修改狀態,dispatch 類似,但是 dispatch 需要返回一個 Promise 給使用者用於處理非同步結果。

(學習視訊分享:、)

以上就是【整理彙總】45+個Vue面試題,帶你鞏固知識點!的詳細內容,更多請關注TW511.COM其它相關文章!