2020年Web前端面試題之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

  • View 接收用戶交互請求
  • View 將請求轉交給ViewModel
  • ViewModel 操作Model數據更新
  • Model 更新完數據,通知ViewModel數據發生變化
  • ViewModel 更新View數據


MVC

  • View 接受用戶交互請求
  • View 將請求轉交給Controller處理
  • Controller 操作Model進行數據更新保存
  • 數據更新保存之後,Model會通知View更新
  • View 更新變化數據使用戶得到反饋

image.pngMVVM模式和MVC有些類似,但有以下不同

  • ViewModel 替換了 Controller,在UI層之下
  • ViewModel 向 View 暴露它所需要的數據和指令對象
  • ViewModel 接收來自 Model 的數據

概括起來,MVVM是由MVC發展而來,通過在Model之上而在View之下增加一個非視覺的組件將來自Model的數據映射到View中。
image.png
image.png


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">

image.png


7.VUE和REACT 的區別?

react整體是函數式的思想,把組件設計成純組件,狀態和邏輯通過參數傳入,所以在react中,是單向數據流;
vue的思想是響應式的,也就是基於是數據可變的,通過對每一個屬性建立Watcher來監聽,當屬性變化的時候,響應式的更新對應的虛擬dom。
具體參照:
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 重寫。


image.png


9. Vue 組件 data 爲什麼必須是函數 ?


因爲組件是可以複用的,JS 裏對象是引用關係,如果組件 data 是一個對象,那麼子組件中的 data 屬性值會互相污染,產生副作用。
所以一個組件的 data 選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝。new Vue 的實例是不會被複用的,因此不存在以上問題。


10、談談你對 keep-alive 的瞭解?

keep-alive 是 Vue 內置的一個組件,可以使被包含的組件保留狀態,避免重新渲染 ,其有以下特性:

  • 一般結合路由和動態組件一起使用,用於緩存組件;
  • 提供 include 和 exclude 屬性,兩者都支持字符串或正則表達式, include 表示只有名稱匹配的組件會被緩存,exclude 表示任何名稱匹配的組件都不會被緩存 ,其中 exclude 的優先級比 include 高;
  • 對應兩個鉤子函數 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,

  • hash: 使用 URL hash 值來作路由。支持所有瀏覽器,包括不支持 HTML5 History Api 的瀏覽器;
  • history : 依賴 HTML5 History API 和服務器配置。具體可以查看 HTML5 History 模式;
  • abstract : 支持所有 JavaScript 運行環境,如 Node.js 服務器端。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式.

1)hash 模式的實現原理
早期的前端路由的實現就是基於 location.hash 來實現的。其實現原理很簡單,location.hash 的值就是 URL 中 # 後面的內容。比如下面這個網站,它的 location.hash 的值爲 ‘#search’:
hash 路由模式的實現主要是基於下面幾個特性:

  • URL 中 hash 值只是客戶端的一種狀態,也就是說當向服務器端發出請求時,hash 部分不會被髮送;
  • hash 值的改變,都會在瀏覽器的訪問歷史中增加一個記錄。因此我們能通過瀏覽器的回退、前進按鈕控制hash 的切換;
  • 可以通過 a 標籤,並設置 href 屬性,當用戶點擊這個標籤後,URL 的 hash 值會發生改變;或者使用 JavaScript 來對 loaction.hash 進行賦值,改變 URL 的 hash 值;
  • 我們可以使用 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 路由模式的實現主要基於存在下面幾個特性:

  • pushState 和 repalceState 兩個 API 來操作實現 URL 的變化 ;
  • 我們可以使用 popstate 事件來監聽 url 的變化,從而對頁面進行跳轉(渲染);
  • history.pushState() 或 history.replaceState() 不會觸發 popstate 事件,這時我們需要手動觸發頁面跳轉(渲染)。


導航鉤子函數(導航守衛)?

  • 全局守衛
  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 全局後置鉤子');
    });


  • 路由獨享的守衛 你可以在路由配置上直接定義 beforeEnter 守衛
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

  • 組件內的守衛 你可以在路由組件內直接定義以下路由導航守衛
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 項目性能優化 — 實踐指南

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章