17.拓展:Vue.js 面試、常見問題答疑

拓展:Vue.js 面試、常見問題答疑

在過去的很多面試中,我會經(jīng)常問候選人一些關于 Vue.js 的問題。這些問題從題面來看很簡單,但仔細想又不是那么簡單,不同的人,會答出不同的層次,從而更好地了解一個人對 Vue.js 的理解程度。

題目

v-show 與 v-if 區(qū)別

第一題應該是最簡單的,提這個問題,也是想讓候選人不那么緊張,因為但凡用過 Vue.js,多少知道 v-showv-if 的區(qū)別,否則就沒得聊了。不過這最簡單的一道題,有三個層次,我會逐一追問。首先,基本所有人都會說到:

v-show 只是 CSS 級別的 display: none;display: block; 之間的切換,而 v-if 決定是否會選擇代碼塊的內(nèi)容(或組件)。

回答這些,已經(jīng)可以得到 50 分了,緊接著我會追問,什么時候用 v-show,什么時候用 v-if ?到這里一部分人會比較吞吐,可能是知道,但表達不出來。我比較傾向的回答是:

頻繁操作時,使用 v-show,一次性渲染完的,使用 v-if,只要意思對就好。

第二問可以得到 80 分了,最后一問很少有人能答上:**那使用 v-if 在性能優(yōu)化上有什么經(jīng)驗?**這是一個加分項,要對 Vue.js 的組件編譯有一定的理解。說一下期望的答案:

因為當 v-if="false" 時,內(nèi)部組件是不會渲染的,所以在特定條件才渲染部分組件(或內(nèi)容)時,可以先將條件設置為 false,需要時(或異步,比如 $nextTick)再設置為 true,這樣可以優(yōu)先渲染重要的其它內(nèi)容,合理利用,可以進行性能優(yōu)化。

綁定 class 的數(shù)組用法

動態(tài)綁定 class 應該不陌生吧,這也是最基本的,但是這個問題卻有點繞,什么叫**綁定 class 的數(shù)組用法?**我們看一下,最常用的綁定 class 怎么寫:

<template>
  <div :class="{show: isShow}">內(nèi)容</div>
</template>
<script>
  export default {
    data () {
      return {
        isShow: true
      }
    }
  }
</script>

綁定 class 的對象用法能滿足大部分業(yè)務需求,不過,在復雜的場景下,會用到數(shù)組,來看示例:

<template>
  <div :class="classes"></div>
</template>
<script>
  export default {
    computed: {
      classes () {
        return [
          `${prefixCls}`,
          `${prefixCls}-${this.type}`,
          {
            [`${prefixCls}-long`]: this.long,
            [`${prefixCls}-${this.shape}`]: !!this.shape,
            [`${prefixCls}-${this.size}`]: this.size !== 'default',
            [`${prefixCls}-loading`]: this.loading != null && this.loading,
            [`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || !!this.customIcon || this.loading),
            [`${prefixCls}-ghost`]: this.ghost
          }
        ];
      }
    }
  }
</script>

示例來自 iView 的 Button 組件,可以看到,數(shù)組里,可以是固定的值,還有動態(tài)值(對象)的混合。

計算屬性和 watch 的區(qū)別

回答該題前,一般都會思考一下。很多人會偏題,直接去答計算屬性和 watch 怎么用,這是不得分的,因為題目是問區(qū)別,并不是用法。

計算屬性是自動監(jiān)聽依賴值的變化,從而動態(tài)返回內(nèi)容,監(jiān)聽是一個過程,在監(jiān)聽的值變化時,可以觸發(fā)一個回調(diào),并做一些事情。

所以區(qū)別來源于用法,只是需要動態(tài)值,那就用計算屬性;需要知道值的改變后執(zhí)行業(yè)務邏輯,才用 watch,用反或混用雖然可行,但都是不正確的用法。

這個問題會延伸出幾個問題:

  1. computed 是一個對象時,它有哪些選項?

  2. computed 和 methods 有什么區(qū)別?

  3. computed 是否能依賴其它組件的數(shù)據(jù)?

  4. watch 是一個對象時,它有哪些選項?

問題 1,已經(jīng)在 16 小節(jié)介紹過,有 get 和 set 兩個選項。

問題 2,methods 是一個方法,它可以接受參數(shù),而 computed 不能;computed 是可以緩存的,methods 不會;一般在 v-for 里,需要根據(jù)當前項動態(tài)綁定值時,只能用 methods 而不能用 computed,因為 computed 不能傳參。

問題 3,computed 可以依賴其它 computed,甚至是其它組件的 data。

問題 4,第 16 小節(jié)也有提到,有以下常用的配置:

  • handler 執(zhí)行的函數(shù)
  • deep 是否深度
  • immediate 是否立即執(zhí)行

事件修飾符

這個問題我會先寫一段代碼:

<custom-component>內(nèi)容</custom-component>

然后問:怎樣給這個自定義組件 custom-component 綁定一個原生的 click 事件?

我一開始并不會問什么是事件修飾符,但是如果候選人說 <custom-component @click="xxx">,就已經(jīng)錯了,說明它對這個沒有概念。這里的 @click 是自定義事件 click,并不是原生事件 click。綁定原生的 click 是這樣的:

<custom-component @click.native="xxx">內(nèi)容</custom-component>

該問題會引申很多,比如常見的事件修飾符有哪些?如果你能說上 .exact,說明你是個很愛探索的人,會大大加分哦。

.exact 是 Vue.js 2.5.0 新加的,它允許你控制由精確的系統(tǒng)修飾符組合觸發(fā)的事件,比如:

<!-- 即使 Alt 或 Shift 被一同按下時也會觸發(fā) -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的時候才觸發(fā) -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 沒有任何系統(tǒng)修飾符被按下的時候才觸發(fā) -->
<button @click.exact="onClick">A</button>

你可能還需要了解常用的幾個事件修飾符:

  • .stop
  • .prevent
  • .capture
  • .self

而且,事件修飾符在連用時,是有先后順序的。

組件中 data 為什么是函數(shù)

為什么組件中的 data 必須是一個函數(shù),然后 return 一個對象,而 new Vue 實例里,data 可以直接是一個對象?

因為組件是用來復用的,JS 里對象是引用關系,這樣作用域沒有隔離,而 new Vue 的實例,是不會被復用的,因此不存在引用對象的問題。

keep-alive 的理解

這是個概念題,主要考察候選人是否知道這個用法。簡單說,就是把一個組件的編譯緩存起來。在第 14 節(jié)有過詳細介紹,也可以看看 Vue.js 的文檔。

遞歸組件的要求

回答這道題,首先你得知道什么是遞歸組件。而不到 10% 的人知道遞歸組件。其實在實際業(yè)務中用的確實不多,在獨立組件中會經(jīng)常使用,第 14 節(jié)和 15 節(jié)專門講過遞歸組件。那回到問題,遞歸組件的要求是什么?主要有兩個:

  • 要給組件設置 name;
  • 要有一個明確的結束條件。

自定義組件的語法糖 v-model 是怎樣實現(xiàn)的

在第 16 節(jié)已經(jīng)詳細介紹過,這里的 v-model,并不是給普通輸入框 <input /> 用的那種 v-model,而是在自定義組件上使用。既然是語法糖,就能夠還原,我們先還原一下:

<template>
  <div>
    {{ currentValue }}
    <button @click="handleClick">Click</button>
  </div>
</template>
<script>
  export default {
    props: {
      value: {
        type: Number,
        default: 0
      }
    },
    data () {
      return {
        currentValue: this.value
      }
    },
    methods: {
      handleClick () {
        this.currentValue += 1;
        this.$emit('input', this.currentValue);
      }
    },
    watch: {
      value (val) {
        this.currentValue = val;
      }
    }
  }
</script>

這個組件中,只有一個 props,但是名字叫 value,內(nèi)部還有一個 currentValue,當改變 currentValue 時,會觸發(fā)一個自定義事件 @input,并把 currentValue 的值返回。這就是一個 v-model 的語法糖,它要求 props 有一個叫 value 的項,同時觸發(fā)的自定義事件必須叫 input。這樣就可以在自定義組件上用 v-model 了:

<custom-component v-model="value"></custom-component>

如果你能說到 model 選項,絕對是加分的。

Vuex 中 mutations 和 actions 的區(qū)別

主要的區(qū)別是,actions 可以執(zhí)行異步。actions 是調(diào)用 mutations,而 mutations 來修改 store。

Render 函數(shù)

這是比較難的一題了,因為很少有人會去了解 Vue.js 的 Render 函數(shù),因為基本用不到。Render 函數(shù)的內(nèi)容本小冊已經(jīng)很深入的講解過了,遇到這個問題,一般可以從這幾個方面來回答:

  • 什么是 Render 函數(shù),它的使用場景是什么。
  • createElement 是什么?
  • Render 函數(shù)有哪些常用的參數(shù)?

說到 Render 函數(shù),就要說到虛擬 DOM(Virtual DOM),Virtual DOM 并不是真正意義上的 DOM,而是一個輕量級的 JavaScript 對象,在狀態(tài)發(fā)生變化時,Virtual DOM 會進行 Diff 運算,來更新只需要被替換的 DOM,而不是全部重繪。

它的使用場景,就是完全發(fā)揮 JavaScript 的編程能力,有時需要結合 JSX 來使用。

createElement 是 Render 函數(shù)的核心,它構成了 Vue Virtual DOM 的模板,它有 3 個參數(shù):

createElement () {
  // {String | Object | Function}
  // 一個 HTML 標簽,組件選項,或一個函數(shù)
  // 必須 return 上述其中一個
  'div',
    // {Object}
    // 一個對應屬性的數(shù)據(jù)對象,可選
    // 您可以在 template 中使用
    {
    // 詳細的屬性
  },
    // {String | Array}
    // 子節(jié)點(VNodes),可選
    [
    createElement('h1', 'hello world'),
    createElement(MyComponent, {
      props: {
        someProps: 'foo'
      }
    }),
    'bar'
  ]
}

常用的參數(shù),主要是指上面第二個參數(shù)里的值了,這個比較多,得去看 Vue.js 的文檔。

怎樣理解單向數(shù)據(jù)流

這個概念出現(xiàn)在組件通信。父組件是通過 prop 把數(shù)據(jù)傳遞到子組件的,但是這個 prop 只能由父組件修改,子組件不能修改,否則會報錯。子組件想修改時,只能通過 $emit 派發(fā)一個自定義事件,父組件接收到后,由父組件修改。

一般來說,對于子組件想要更改父組件狀態(tài)的場景,可以有兩種方案:

  1. 在子組件的 data 中拷貝一份 prop,data 是可以修改的,但 prop 不能:

    export default {
      props: {
        value: String
      },
      data () {
        return {
          currentValue: this.value
        }
      }
    }
    
    
  2. 如果是對 prop 值的轉換,可以使用計算屬性:

    export default {
      props: ['size'],
      computed: {
        normalizedSize: function () {
          return this.size.trim().toLowerCase();
        }
      }
    }
    
    

如果你能提到 v-model 實現(xiàn)數(shù)據(jù)的雙向綁定、.sync 用法,會大大加分的,這些在第 16 節(jié)已經(jīng)詳細介紹過。

生命周期

Vue.js 生命周期 主要有 8 個階段:

  • 創(chuàng)建前 / 后(beforeCreate / created):在 beforeCreate 階段,Vue 實例的掛載元素 el 和數(shù)據(jù)對象 data 都為 undefined,還未初始化。在 created 階段,Vue 實例的數(shù)據(jù)對象 data 有了,el 還沒有。

  • 載入前 / 后(beforeMount / mounted):在 beforeMount 階段,Vue 實例的 $el 和 data 都初始化了,但還是掛載之前為虛擬的 DOM 節(jié)點,data 尚未替換。在 mounted 階段,Vue 實例掛載完成,data 成功渲染。

  • 更新前 / 后(beforeUpdate / updated):當 data 變化時,會觸發(fā) beforeUpdate 和 updated 方法。這兩個不常用,且不推薦使用。

  • 銷毀前 / 后(beforeDestroy / destroyed):beforeDestroy 是在 Vue 實例銷毀前觸發(fā),一般在這里要通過 removeEventListener 解除手動綁定的事件。實例銷毀后,觸發(fā) destroyed。

組件間通信

本小冊一半的篇幅都在講組件的通信,如果能把這些都吃透,基本上 Vue.js 的面試就穩(wěn)了。

這個問題看似簡單,卻比較大,回答時,可以拆分為幾種場景:

  1. 父子通信:

    父向子傳遞數(shù)據(jù)是通過 props,子向父是通過 events(emit);通過父鏈 / 子鏈也可以通信(parent / $children);ref 也可以訪問組件實例;provide / inject API。

  2. 兄弟通信:

    Bus;Vuex;

  3. 跨級通信:

    Bus;Vuex;provide / inject API。

除了常規(guī)的通信方法,本冊介紹的 dispatch / broadcast 和 findComponents 系列方法也可以說的,如果能說到這些,說明你對 Vue.js 組件已經(jīng)有較深入的研究。

路由的跳轉方式

一般有兩種:

  1. 通過 <router-link to="home">,router-link 標簽會渲染為 <a> 標簽,在 template 中的跳轉都是用這種;
  2. 另一種是編程式導航,也就是通過 JS 跳轉,比如 router.push('/home')。

Vue.js 2.x 雙向綁定原理

這個問題幾乎是面試必問的,回答也是有深有淺。基本上要知道核心的 API 是通過 Object.defineProperty() 來劫持各個屬性的 setter / getter,在數(shù)據(jù)變動時發(fā)布消息給訂閱者,觸發(fā)相應的監(jiān)聽回調(diào),這也是為什么 Vue.js 2.x 不支持 IE8 的原因(IE 8 不支持此 API,且無法通過 polyfill 實現(xiàn))。

Vue.js 文檔已經(jīng)對 深入響應式原理 解釋的很透徹了。

什么是 MVVM,與 MVC 有什么區(qū)別

MVVM 模式是由經(jīng)典的軟件架構 MVC 衍生來的。當 View(視圖層)變化時,會自動更新到 ViewModel(視圖模型),反之亦然。View 和 ViewModel 之間通過雙向綁定(data-binding)建立聯(lián)系。與 MVC 不同的是,它沒有 Controller 層,而是演變?yōu)?ViewModel。

ViewModel 通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來,而 View 和 Model 之間的同步工作是由 Vue.js 完成的,我們不需要手動操作 DOM,只需要維護好數(shù)據(jù)狀態(tài)。

結語

一個人的簡歷,是由簡單到復雜再到簡單,技術是無止盡的,接觸的越多,越能感到自己的渺小。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 主要還是自己看的,所有內(nèi)容來自官方文檔。 介紹 Vue.js 是什么 Vue (讀音 /vju?/,類似于 vie...
    Leonzai閱讀 3,540評論 0 25
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 簡單小巧、漸進式、功能強大的技術棧 1.1.2 為什么學習...
    蔡華鵬閱讀 3,497評論 0 3
  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,373評論 0 6
  • Vue.js是什么 Vue.js(讀音 /vju?/, 類似于 view) 是一套構建用戶界面的 漸進式框架。與其...
    魚魚吃貓貓閱讀 3,398評論 1 12
  • 本章內(nèi)容:表單 與 v-model、組件、自定義指令 六、表單 與 v-model 6.1、基本用法 Vue.js...
    了凡和纖風閱讀 979評論 1 2

友情鏈接更多精彩內(nèi)容