天天看點

Vue 面試[1]

文章目錄

  • ​​1. Vue的基本原理​​
  • ​​2. 雙向資料綁定的原理​​
  • ​​3. 使用 Object.defineProperty() 來進行資料劫持有什麼缺點?​​
  • ​​4. MVVM、MVC、MVP的差別​​
  • ​​5. Computed 和 Watch 的差別​​
  • ​​6. Computed 和 Methods 的差別​​
  • ​​7. slot是什麼?有什麼作用?原理是什麼?​​
  • ​​8. 過濾器的作用,如何實作一個過濾器​​
  • ​​9. 如何儲存頁面的目前的狀态​​
  • ​​10. 常見的事件修飾符及其作用​​
  • ​​11. v-if、v-show、v-html 的原理​​
  • ​​13. v-if和v-show的差別​​

1. Vue的基本原理

當一個Vue執行個體建立時,Vue會周遊data中的屬性,用 Object.defineProperty(vue3.0使用proxy )将它們轉為 getter/setter,并且在内部追蹤相關依賴,在屬性被通路和修改時通知變化。 每個元件執行個體都有相應的 watcher 程式執行個體,它會在元件渲染的過程中把屬性記錄為依賴,之後當依賴項的setter被調用時,會通知watcher重新計算,進而緻使它關聯的元件得以更新。

Vue 面試[1]

2. 雙向資料綁定的原理

Vue.js 是采用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出消息給訂閱者,觸發相應的監聽回調。主要分為以下幾個步驟:

  1. 需要observe的資料對象進行遞歸周遊,包括子屬性對象的屬性,都加上setter和getter這樣的話,給這個對象的某個值指派,就會觸發setter,那麼就能監聽到了資料變化
  2. compile解析模闆指令,将模闆中的變量替換成資料,然後初始化渲染頁面視圖,并将每個指令對應的節點綁定更新函數,添加監聽資料的訂閱者,一旦資料有變動,收到通知,更新視圖
  3. Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是: ①在自身執行個體化時往屬性訂閱器(dep)裡面添加自己 ②自身必須有一個update()方法 ③待屬性變動dep.notice()通知時,能調用自身的update()方法,并觸發Compile中綁定的回調,則功成身退。
  4. MVVM作為資料綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model資料變化,通過Compile來解析編譯模闆指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達到資料變化 -> 視圖更新;視圖互動變化(input) -> 資料model變更的雙向綁定效果。
    Vue 面試[1]

3. 使用 Object.defineProperty() 來進行資料劫持有什麼缺點?

在對一些屬性進行操作時,使用這種方法無法攔截,比如通過下标方式修改數組資料或者給對象新增屬性,這都不能觸發元件的重新渲染,因為 Object.defineProperty 不能攔截到這些操作。更精确的來說,對于數組而言,大部分操作都是攔截不到的,隻是 Vue 内部通過重寫函數的方式解決了這個問題。

在 Vue3.0 中已經不使用這種方式了,而是通過使用 Proxy 對對象進行代理,進而實作資料劫持。使用Proxy 的好處是它可以完美的監聽到任何方式的資料改變,唯一的缺點是相容性的問題,因為 Proxy 是 ES6 的文法。

4. MVVM、MVC、MVP的差別

MVC、MVP 和 MVVM 是三種常見的軟體架構設計模式,主要通過分離關注點的方式來組織代碼結構,優化開發效率。

(1)MVC

MVC 通過分離 Model、View 和 Controller 的方式來組織代碼結構。其中 View 負責頁面的顯示邏輯,Model 負責存儲頁面的業務資料,以及對相應資料的操作。并且 View 和 Model 應用了觀察者模式,當 Model 層發生改變的時候它會通知有關 View 層更新頁面。Controller 層是 View 層和 Model 層的紐帶,它主要負責使用者與應用的響應操作,當使用者與頁面産生互動的時候,Controller 中的事件觸發器就開始工作了,通過調用 Model 層,來完成對 Model 的修改,然後 Model 層再去通知 View 層更新.

(2)MVVM

MVVM 分為 Model、View、ViewModel:

  • Model代表資料模型,資料和業務邏輯都在Model層中定義;
  • View代表UI視圖,負責資料的展示;
  • ViewModel負責監聽Model中資料的改變并且控制視圖的更新,處理使用者互動操作;

Model和View并無直接關聯,而是通過ViewModel來進行聯系的,Model和ViewModel之間有着雙向資料綁定的聯系。是以當Model中的資料改變時會觸發View層的重新整理,View中由于使用者互動操作而改變的資料也會在Model中同步。

這種模式實作了 Model和View的資料自動同步,是以開發者隻需要專注于資料的維護操作即可,而不需要自己操作DOM。

Vue 面試[1]

(3)MVP

MVP 模式與 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用觀察者模式,來實作當 Model 層資料發生變化的時候,通知 View 層的更新。這樣 View 層和 Model 層耦合在一起,當項目邏輯變得複雜的時候,可能會造成代碼的混亂,并且可能會對代碼的複用性造成一些問題。MVP 的模式通過使用 Presenter 來實作對 View 層和 Model 層的解耦。MVC 中的Controller 隻知道 Model 的接口,是以它沒有辦法控制 View 層的更新,MVP 模式中,View 層的接口暴露給了 Presenter 是以可以在 Presenter 中将 Model 的變化和 View 的變化綁定在一起,以此來實作 View 和 Model 的同步更新。這樣就實作了對 View 和 Model 的解耦,Presenter 還包含了其他的響應邏輯。

5. Computed 和 Watch 的差別

對于Computed:

  • 支援緩存,隻有依賴的資料發生變化,才重新計算
  • 不支援異步,當computed存在異步的時候,無法監聽資料
  • computed預設走緩存,計算屬性是基于他們的響應式依賴進行緩存的,也就是data聲明的,props傳遞過來的資料進行計算的
  • 如果一個屬性是基于其他屬性的時候,那麼這個屬性依賴的屬性也會使用computed
  • 如果computed屬性的屬性值是函數,那麼預設使用get方法,函數的傳回值就是屬性的屬性值;在computed中,屬性有一個get方法和一個set方法,當資料發生變化時,會調用set方法。

對于watch:

  • 不支援緩存
  • 支援異步
  • 監聽的函數接收兩個參數,第一個參數是最新的值,第二個是變化之前的值
  • 當一個屬性發生變化時,就需要執行相應的操作
  • 監聽資料必須是data中聲明的或者父元件傳遞過來的props中的資料,當發生變化時,會觸發其他操作,函數有兩個的參數:

    immediate:元件加載立即觸發回調函數

    deep:深度監聽,發現資料内部的變化,在複雜資料類型中使用,例如數組中的對象發生變化。需要注意的是,deep無法監聽到數組和對象内部的變化。

當想要執行異步或者昂貴的操作以響應不斷的變化時,就需要使用watch。

總結:

總結:

  • computed 計算屬性 : 依賴其它屬性值,并且 computed 的值有緩存,隻有它依賴的屬性值發生改變,下一次擷取 computed 的值時才會重新計算 computed 的值。
  • watch 偵聽器 : 更多的是觀察的作用,無緩存性,類似于某些資料的監聽回調,每當監聽的資料變化時都會執行回調進行後續操作。

運用場景:

  • 當需要進行數值計算,并且依賴于其它資料時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次擷取值時都要重新計算。
  • 當需要在資料變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許執行異步操作 ( 通路一個 API ),限制執行該操作的頻率,并在得到最終結果前,設定中間狀态。這些都是計算屬性無法做到的。

6. Computed 和 Methods 的差別

可以将同一函數定義為一個 method 或者一個計算屬性。對于最終的結果,兩種方式是相同的

不同點:

  • computed: 計算屬性是基于它們的依賴進行緩存的,隻有在它的相關依賴發生改變時才會重新求值;
  • method 調用總會執行該函數。

7. slot是什麼?有什麼作用?原理是什麼?

slot又名插槽,是Vue的内容分發機制,元件内部的模闆引擎使用slot元素作為承載分發内容的出口。插槽slot是子元件的一個模闆标簽元素,而這一個标簽元素是否顯示,以及怎麼顯示是由父元件決定的。slot又分三類,預設插槽,具名插槽和作用域插槽。

  • 預設插槽:又名匿名查抄,當slot沒有指定name屬性值的時候一個預設顯示插槽,一個元件内隻有有一個匿名插槽。
  • 具名插槽:帶有具體名字的插槽,也就是帶有name屬性的slot,一個元件可以出現多個具名插槽。
  • 作用域插槽:預設插槽、具名插槽的一個變體,可以是匿名插槽,也可以是具名插槽,該插槽的不同點是在子元件渲染作用域插槽時,可以将子元件内部的資料傳遞給父元件,讓父元件根據子元件的傳遞過來的資料決定如何渲染該插槽。

實作原理:當子元件vm執行個體化時,擷取到父元件傳入的slot标簽的内容,存放在vm.slot.default,具名插槽為vm.slot中的内容進行替換,此時可以為插槽傳遞資料,若存在資料,則可稱該插槽為作用域插槽。

8. 過濾器的作用,如何實作一個過濾器

根據過濾器的名稱,過濾器是用來過濾資料的,在Vue中使用filters來過濾資料,filters不會修改資料,而是過濾資料,改變使用者看到的輸出(計算屬性 computed ,方法 methods 都是通過修改資料來處理資料格式的輸出顯示)。

使用場景:

  • 需要格式化資料的情況,比如需要處理時間、價格等資料格式的輸出 / 顯示。
  • 比如後端傳回一個 年月日的日期字元串,前端需要展示為 多少天前 的資料格式,此時就可以用fliters過濾器來處理資料。

過濾器是一個函數,它會把表達式中的值始終當作函數的第一個參數。過濾器用在插值表達式 {{ }} 和 v-bind 表達式 中,然後放在操作符“ | ”後面進行訓示。

例如,在顯示金額,給商品價格添加機關:

  • 商品價格:{{item.price | filterPrice}}
  • filters: {

    filterPrice (price) {

    return price ? ('¥' + price) : '--'

    }

    }

    9. 如何儲存頁面的目前的狀态

    既然是要保持頁面的狀态(其實也就是元件的狀态),那麼會出現以下兩種情況:

    • 前元件會被解除安裝
    • 前元件不會被解除安裝

    元件會被解除安裝:

    (1)将狀态存儲在LocalStorage / SessionStorage

    隻需要在元件即将被銷毀的生命周期 componentWillUnmount (react)中在 LocalStorage / SessionStorage 中把目前元件的 state 通過 JSON.stringify() 儲存下來就可以了。在這裡面需要注意的是元件更新狀态的時機。

    比如從 B 元件跳轉到 A 元件的時候,A 元件需要更新自身的狀态。但是如果從别的元件跳轉到 B 元件的時候,實際上是希望 B 元件重新渲染的,也就是不要從 Storage 中讀取資訊。是以需要在 Storage 中的狀态加入一個 flag 屬性,用來控制 A 元件是否讀取 Storage 中的狀态。

    優點:

    • 相容性好,不需要額外庫或工具。
    • 簡單快捷,基本可以滿足大部分需求。

    缺點:

    • 狀态通過 JSON 方法儲存(相當于深拷貝),如果狀态中有特殊情況(比如 Date 對象、Regexp 對象等)的時候會得到字元串而不是原來的值。(具體參考用 JSON 深拷貝的缺點)
    • 如果 B 元件後退或者下一頁跳轉并不是前元件,那麼 flag 判斷會失效,導緻從其他頁面進入 A 元件頁面時 A 元件會重新讀取 Storage,會造成很奇怪的現象

    (2)路由傳值

    通過 react-router 的 Link 元件的 prop —— to 可以實作路由間傳遞參數的效果。

    在這裡需要用到 state 參數,在 B 元件中通過 history.location.state 就可以拿到 state 值,儲存它。傳回 A 元件時再次攜帶 state 達到路由狀态保持的效果。

    優點:

    • 簡單快捷,不會污染 LocalStorage / SessionStorage。
    • 可以傳遞 Date、RegExp 等特殊對象(不用擔心 JSON.stringify / parse 的不足)

    缺點:

    • 如果 A 元件可以跳轉至多個元件,那麼在每一個跳轉元件内都要寫相同的邏輯。

    前元件不會被解除安裝

    (1)單頁面渲染

    要切換的元件作為子元件全屏渲染,父元件中正常儲存頁面狀态。

    優點:

    • 代碼量少
    • 不需要考慮狀态傳遞過程中的錯誤
    • 增加 A 元件維護成本
    • 需要傳入額外的 prop 到 B 元件
    • 無法利用路由定位頁面
    {
      path: '/',
      name: 'xxx',
      component: ()=>import('../src/views/xxx.vue'),
      meta:{
        keepAlive: true // 需要被緩存
      }
    },
          

    10. 常見的事件修飾符及其作用

    1. .stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
    2. prevent :等同于 JavaScript 中的 event.preventDefault() ,防止執行預設的行為(如果事件可取消,則取消該事件,而不停止事件的進一步傳播);
    3. .capture :與事件冒泡的方向相反,事件捕獲由外到内;
    4. .self :隻會觸發自己範圍内的事件,不包含子元素;
    5. .once :隻會觸發一次。

    11. v-if、v-show、v-html 的原理

    • v-if會調用addIfCondition方法,生成vnode的時候會忽略對應節點,render的時候就不會渲染;
    • v-show會生成vnode,render的時候也會渲染成真實節點,隻是在render過程中會在節點的屬性中修改show屬性值,也就是常說的display;
    • v-html會先移除節點下的所有節點,調用html方法,通過addProp添加innerHTML屬性,歸根結底還是設定innerHTML為v-html的值。

    13. v-if和v-show的差別

    • 手段:v-if是動态的向DOM樹内添加或者删除DOM元素;v-show是通過設定DOM元素的display樣式屬性控制顯隐;
    • 編譯過程:v-if切換有一個局部編譯/解除安裝的過程,切換過程中合适地銷毀和重建内部的事件監聽和子元件;v-show隻是簡單的基于css切換;
    • 編譯條件:v-if是惰性的,如果初始條件為假,則什麼也不做;隻有在條件第一次變為真時才開始局部編譯; v-show是在任何條件下,無論首次條件是否為真,都被編譯,然後被緩存,而且DOM元素保留;
    • 性能消耗:v-if有更高的切換消耗;v-show有更高的初始渲染消耗;
    • 使用場景:v-if适合營運條件不大可能改變;v-show适合頻繁切換。