<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          30 道 Vue 面試題,內(nèi)含詳細(xì)講解

          共 14953字,需瀏覽 30分鐘

           ·

          2020-12-23 13:26

          前言

          本文以前端面試官的角度出發(fā),對(duì) Vue 框架中一些重要的特性、框架的原理以問題的形式進(jìn)行整理匯總,意在幫助作者及讀者自測(cè)下 Vue 掌握的程度。本文章節(jié)結(jié)構(gòu)以從易到難進(jìn)行組織,建議讀者按章節(jié)順序進(jìn)行閱讀,當(dāng)然大佬級(jí)別的請(qǐng)隨意。希望讀者讀完本文,有一定的啟發(fā)思考,也能對(duì)自己的 Vue 掌握程度有一定的認(rèn)識(shí),對(duì)缺漏之處進(jìn)行彌補(bǔ),對(duì) Vue 有更好的掌握。文章最后一題,歡迎同學(xué)們積極回答,分享各自的經(jīng)驗(yàn) ~~~

          1、說說你對(duì) SPA 單頁面的理解,它的優(yōu)缺點(diǎn)分別是什么?

          SPA( single-page application )僅在 Web 頁面初始化時(shí)加載相應(yīng)的 HTML、JavaScript 和 CSS。一旦頁面加載完成,SPA 不會(huì)因?yàn)橛脩舻牟僮鞫M(jìn)行頁面的重新加載或跳轉(zhuǎn);取而代之的是利用路由機(jī)制實(shí)現(xiàn) HTML 內(nèi)容的變換,UI 與用戶的交互,避免頁面的重新加載。「優(yōu)點(diǎn):」

          • 用戶體驗(yàn)好、快,內(nèi)容的改變不需要重新加載整個(gè)頁面,避免了不必要的跳轉(zhuǎn)和重復(fù)渲染;
          • 基于上面一點(diǎn),SPA 相對(duì)對(duì)服務(wù)器壓力小;
          • 前后端職責(zé)分離,架構(gòu)清晰,前端進(jìn)行交互邏輯,后端負(fù)責(zé)數(shù)據(jù)處理;

          「缺點(diǎn):」

          • 初次加載耗時(shí)多:為實(shí)現(xiàn)單頁 Web 應(yīng)用功能及顯示效果,需要在加載頁面的時(shí)候?qū)?JavaScript、CSS 統(tǒng)一加載,部分頁面按需加載;
          • 前進(jìn)后退路由管理:由于單頁應(yīng)用在一個(gè)頁面中顯示所有的內(nèi)容,所以不能使用瀏覽器的前進(jìn)后退功能,所有的頁面切換需要自己建立堆棧管理;
          • SEO 難度較大:由于所有的內(nèi)容都在一個(gè)頁面中動(dòng)態(tài)替換顯示,所以在 SEO 上其有著天然的弱勢(shì)。

          2、v-show 與 v-if 有什么區(qū)別?

          「v-if」?是「真正」的條件渲染,因?yàn)樗鼤?huì)確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當(dāng)?shù)乇讳N毀和重建;也是「惰性的」:如果在初始渲染時(shí)條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r(shí),才會(huì)開始渲染條件塊。「v-show」?就簡單得多——不管初始條件是什么,元素總是會(huì)被渲染,并且只是簡單地基于 CSS 的 “display” 屬性進(jìn)行切換。所以,v-if 適用于在運(yùn)行時(shí)很少改變條件,不需要頻繁切換條件的場(chǎng)景;v-show 則適用于需要非常頻繁切換條件的場(chǎng)景。

          3、Class 與 Style 如何動(dòng)態(tài)綁定?

          Class 可以通過對(duì)象語法和數(shù)組語法進(jìn)行動(dòng)態(tài)綁定:

          • 對(duì)象語法:
          "{?active:?isActive,?'text-danger':?hasError?}">

          data:?{
          ??isActive:?true,
          ??hasError:?false
          }
          復(fù)制代碼
          • 數(shù)組語法:
          "[isActive???activeClass?:?'',?errorClass]">

          data:?{
          ??activeClass:?'active',
          ??errorClass:?'text-danger'
          }
          復(fù)制代碼

          Style 也可以通過對(duì)象語法和數(shù)組語法進(jìn)行動(dòng)態(tài)綁定:

          • 對(duì)象語法:
          "{?color:?activeColor,?fontSize:?fontSize?+?'px'?}">

          data:?{
          ??activeColor:?'red',
          ??fontSize:?30
          }
          復(fù)制代碼
          • 數(shù)組語法:
          "[styleColor,?styleSize]">

          data:?{
          ??styleColor:?{
          ?????color:?'red'
          ???},
          ??styleSize:{
          ?????fontSize:'23px'
          ??}
          }
          復(fù)制代碼

          4、怎樣理解 Vue 的單向數(shù)據(jù)流?

          所有的 prop 都使得其父子 prop 之間形成了一個(gè)「單向下行綁定」:父級(jí) prop 的更新會(huì)向下流動(dòng)到子組件中,但是反過來則不行。這樣會(huì)防止從子組件意外改變父級(jí)組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。額外的,每次父級(jí)組件發(fā)生更新時(shí),子組件中所有的 prop 都將會(huì)刷新為最新的值。這意味著你不應(yīng)該在一個(gè)子組件內(nèi)部改變 prop。如果你這樣做了,Vue 會(huì)在瀏覽器的控制臺(tái)中發(fā)出警告。子組件想修改時(shí),只能通過 $emit 派發(fā)一個(gè)自定義事件,父組件接收到后,由父組件修改。有兩種常見的試圖改變一個(gè) prop 的情形 :

          • 「這個(gè) prop 用來傳遞一個(gè)初始值;這個(gè)子組件接下來希望將其作為一個(gè)本地的 prop 數(shù)據(jù)來使用。」?在這種情況下,最好定義一個(gè)本地的 data 屬性并將這個(gè) prop 用作其初始值:
          props:?['initialCounter'],
          data:?function?()?{
          ??return?{
          ????counter:?this.initialCounter
          ??}
          }
          復(fù)制代碼
          • 「這個(gè) prop 以一種原始的值傳入且需要進(jìn)行轉(zhuǎn)換。」?在這種情況下,最好使用這個(gè) prop 的值來定義一個(gè)計(jì)算屬性
          props:?['size'],
          computed:?{
          ??normalizedSize:?function?()?{
          ????return?this.size.trim().toLowerCase()
          ??}
          }
          復(fù)制代碼

          5、computed 和 watch 的區(qū)別和運(yùn)用的場(chǎng)景?

          「computed:」?是計(jì)算屬性,依賴其它屬性值,并且 computed 的值有緩存,只有它依賴的屬性值發(fā)生改變,下一次獲取 computed 的值時(shí)才會(huì)重新計(jì)算 computed 的值;「watch:」?更多的是「觀察」的作用,類似于某些數(shù)據(jù)的監(jiān)聽回調(diào) ,每當(dāng)監(jiān)聽的數(shù)據(jù)變化時(shí)都會(huì)執(zhí)行回調(diào)進(jìn)行后續(xù)操作;「運(yùn)用場(chǎng)景:」

          • 當(dāng)我們需要進(jìn)行數(shù)值計(jì)算,并且依賴于其它數(shù)據(jù)時(shí),應(yīng)該使用 computed,因?yàn)榭梢岳?computed 的緩存特性,避免每次獲取值時(shí),都要重新計(jì)算;

          • 當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開銷較大的操作時(shí),應(yīng)該使用 watch,使用 watch 選項(xiàng)允許我們執(zhí)行異步操作 ( 訪問一個(gè) API ),限制我們執(zhí)行該操作的頻率,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)。這些都是計(jì)算屬性無法做到的。

          6、直接給一個(gè)數(shù)組項(xiàng)賦值,Vue 能檢測(cè)到變化嗎?

          由于 JavaScript 的限制,Vue 不能檢測(cè)到以下數(shù)組的變動(dòng):

          • 當(dāng)你利用索引直接設(shè)置一個(gè)數(shù)組項(xiàng)時(shí),例如:vm.items[indexOfItem] = newValue
          • 當(dāng)你修改數(shù)組的長度時(shí),例如:vm.items.length = newLength

          為了解決第一個(gè)問題,Vue 提供了以下操作方法:

          //?Vue.set
          Vue.set(vm.items,?indexOfItem,?newValue)
          //?vm.$set,Vue.set的一個(gè)別名
          vm.$set(vm.items,?indexOfItem,?newValue)
          //?Array.prototype.splice
          vm.items.splice(indexOfItem,?1,?newValue)
          復(fù)制代碼

          為了解決第二個(gè)問題,Vue 提供了以下操作方法:

          //?Array.prototype.splice
          vm.items.splice(newLength)
          復(fù)制代碼

          7、談?wù)勀銓?duì) Vue 生命周期的理解?

          「(1)生命周期是什么?」Vue 實(shí)例有一個(gè)完整的生命周期,也就是從開始創(chuàng)建、初始化數(shù)據(jù)、編譯模版、掛載 Dom -> 渲染、更新 -> 渲染、卸載等一系列過程,我們稱這是 Vue 的生命周期。「(2)各個(gè)生命周期的作用」

          生命周期描述
          beforeCreate組件實(shí)例被創(chuàng)建之初,組件的屬性生效之前
          created組件實(shí)例已經(jīng)完全創(chuàng)建,屬性也綁定,但真實(shí) dom 還沒有生成,$el 還不可用
          beforeMount在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用
          mountedel 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用該鉤子
          beforeUpdate組件數(shù)據(jù)更新之前調(diào)用,發(fā)生在虛擬 DOM 打補(bǔ)丁之前
          update組件數(shù)據(jù)更新之后
          activitedkeep-alive 專屬,組件被激活時(shí)調(diào)用
          deactivatedkeep-alive 專屬,組件被銷毀時(shí)調(diào)用
          beforeDestory組件銷毀前調(diào)用
          destoryed組件銷毀后調(diào)用

          「(3)生命周期示意圖」

          8、Vue 的父組件和子組件生命周期鉤子函數(shù)執(zhí)行順序?

          Vue 的父組件和子組件生命周期鉤子函數(shù)執(zhí)行順序可以歸類為以下 4 部分:

          • 加載渲染過程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

          • 子組件更新過程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

          • 父組件更新過程 父 beforeUpdate -> 父 updated

          • 銷毀過程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

          9、在哪個(gè)生命周期內(nèi)調(diào)用異步請(qǐng)求?

          可以在鉤子函數(shù) created、beforeMount、mounted 中進(jìn)行調(diào)用,因?yàn)樵谶@三個(gè)鉤子函數(shù)中,data 已經(jīng)創(chuàng)建,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。但是本人推薦在 created 鉤子函數(shù)中調(diào)用異步請(qǐng)求,因?yàn)樵?created 鉤子函數(shù)中調(diào)用異步請(qǐng)求有以下優(yōu)點(diǎn):

          • 能更快獲取到服務(wù)端數(shù)據(jù),減少頁面 loading 時(shí)間;
          • ssr 不支持 beforeMount 、mounted 鉤子函數(shù),所以放在 created 中有助于一致性;

          10、在什么階段才能訪問操作DOM?

          在鉤子函數(shù) mounted 被調(diào)用前,Vue 已經(jīng)將編譯好的模板掛載到頁面上,所以在 mounted 中可以訪問操作 DOM。vue 具體的生命周期示意圖可以參見如下,理解了整個(gè)生命周期各個(gè)階段的操作,關(guān)于生命周期相關(guān)的面試題就難不倒你了。

          11、父組件可以監(jiān)聽到子組件的生命周期嗎?

          比如有父組件 Parent 和子組件 Child,如果父組件監(jiān)聽到子組件掛載 mounted 就做一些邏輯處理,可以通過以下寫法實(shí)現(xiàn):

          //?Parent.vue
          "doSomething"/>
          ????
          //?Child.vue
          mounted()?{
          ??this.$emit("mounted");
          }
          復(fù)制代碼

          以上需要手動(dòng)通過 $emit 觸發(fā)父組件的事件,更簡單的方式可以在父組件引用子組件時(shí)通過 @hook 來監(jiān)聽即可,如下所示:

          //??Parent.vue
          "doSomething"?>
          doSomething()?{
          ???console.log('父組件監(jiān)聽到?mounted?鉤子函數(shù)?...');
          },
          ????
          //??Child.vue
          mounted(){
          ???console.log('子組件觸發(fā)?mounted?鉤子函數(shù)?...');
          },????
          ????
          //?以上輸出順序?yàn)椋?br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">//?子組件觸發(fā)?mounted?鉤子函數(shù)?...
          //?父組件監(jiān)聽到?mounted?鉤子函數(shù)?...?????
          復(fù)制代碼

          當(dāng)然 @hook 方法不僅僅是可以監(jiān)聽 mounted,其它的生命周期事件,例如:created,updated 等都可以監(jiān)聽。

          12、談?wù)勀銓?duì) keep-alive 的了解?

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

          • 一般結(jié)合路由和動(dòng)態(tài)組件一起使用,用于緩存組件;
          • 提供 include 和 exclude 屬性,兩者都支持字符串或正則表達(dá)式, include 表示只有名稱匹配的組件會(huì)被緩存,exclude 表示任何名稱匹配的組件都不會(huì)被緩存 ,其中 exclude 的優(yōu)先級(jí)比 include 高;
          • 對(duì)應(yīng)兩個(gè)鉤子函數(shù) activated 和 deactivated ,當(dāng)組件被激活時(shí),觸發(fā)鉤子函數(shù) activated,當(dāng)組件被移除時(shí),觸發(fā)鉤子函數(shù) deactivated。

          13、組件中 data 為什么是一個(gè)函數(shù)?

          ?

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

          ?
          //?data
          data()?{
          ??return?{
          ?message:?"子組件",
          ?childName:this.name
          ??}
          }
          //?new?Vue
          new?Vue({
          ??el:?'#app',
          ??router,
          ??template:?'',
          ??components:?{App}
          })
          復(fù)制代碼

          因?yàn)榻M件是用來復(fù)用的,且 JS 里對(duì)象是引用關(guān)系,如果組件中 data 是一個(gè)對(duì)象,那么這樣作用域沒有隔離,子組件中的 data 屬性值會(huì)相互影響,如果組件中 data 選項(xiàng)是一個(gè)函數(shù),那么每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝,組件實(shí)例之間的 data 屬性值不會(huì)互相影響;而 new Vue 的實(shí)例,是不會(huì)被復(fù)用的,因此不存在引用對(duì)象的問題。

          14、v-model 的原理?

          我們?cè)?vue 項(xiàng)目中主要使用 v-model 指令在表單 input、textarea、select 等元素上創(chuàng)建雙向數(shù)據(jù)綁定,我們知道 v-model 本質(zhì)上不過是語法糖,v-model 在內(nèi)部為不同的輸入元素使用不同的屬性并拋出不同的事件:

          • text 和 textarea 元素使用 value 屬性和 input 事件;
          • checkbox 和 radio 使用 checked 屬性和 change 事件;
          • select 字段將 value 作為 prop 并將 change 作為事件。

          以 input 表單元素為例:

          'something'>
          ????
          相當(dāng)于
          "something"?v-on:input="something?=?$event.target.value">
          復(fù)制代碼

          如果在自定義組件中,v-model 默認(rèn)會(huì)利用名為 value 的 prop 和名為 input 的事件,如下所示:

          父組件:
          "message">
          子組件:
          {{value}}

          props:{
          ????value:?String
          },
          methods:?{
          ??test1(){
          ?????this.$emit('input',?'小紅')
          ??},
          },
          復(fù)制代碼

          15、Vue 組件間通信有哪幾種方式?

          Vue 組件間通信是面試常考的知識(shí)點(diǎn)之一,這題有點(diǎn)類似于開放題,你回答出越多方法當(dāng)然越加分,表明你對(duì) Vue 掌握的越熟練。Vue 組件間通信只要指以下 3 類通信:父子組件通信、隔代組件通信、兄弟組件通信,下面我們分別介紹每種通信方式且會(huì)說明此種方法可適用于哪類組件間通信。「(1)props / $emit?適用 父子組件通信」這種方法是 Vue 組件的基礎(chǔ),相信大部分同學(xué)耳聞能詳,所以此處就不舉例展開介紹。「(2)ref?與?$parent / $children?適用 父子組件通信」

          • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件實(shí)例
          • $parent?/?$children:訪問父 / 子實(shí)例

          「(3)EventBus ($emit / $on)?適用于 父子、隔代、兄弟組件通信」這種方法通過一個(gè)空的 Vue 實(shí)例作為中央事件總線(事件中心),用它來觸發(fā)事件和監(jiān)聽事件,從而實(shí)現(xiàn)任何組件間的通信,包括父子、隔代、兄弟組件。「(4)$attrs/$listeners?適用于 隔代組件通信」

          • $attrs:包含了父作用域中不被 prop 所識(shí)別 (且獲取) 的特性綁定 ( class 和 style 除外 )。當(dāng)一個(gè)組件沒有聲明任何 prop 時(shí),這里會(huì)包含所有父作用域的綁定 ( class 和 style 除外 ),并且可以通過?v-bind="$attrs"?傳入內(nèi)部組件。通常配合 inheritAttrs 選項(xiàng)一起使用。
          • $listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽器。它可以通過?v-on="$listeners"?傳入內(nèi)部組件

          「(5)provide / inject?適用于 隔代組件通信」祖先組件中通過 provider 來提供變量,然后在子孫組件中通過 inject 來注入變量。provide / inject API 主要解決了跨級(jí)組件間的通信問題,不過它的使用場(chǎng)景,主要是子組件獲取上級(jí)組件的狀態(tài),跨級(jí)組件間建立了一種主動(dòng)提供與依賴注入的關(guān)系。「(6)Vuex 適用于 父子、隔代、兄弟組件通信」Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉庫)。“store” 基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )。

          • Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
          • 改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化。

          16、你使用過 Vuex 嗎?

          Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉庫)。“store” 基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )。(1)Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。(2)改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化。主要包括以下幾個(gè)模塊:

          • State:定義了應(yīng)用狀態(tài)的數(shù)據(jù)結(jié)構(gòu),可以在這里設(shè)置默認(rèn)的初始狀態(tài)。
          • Getter:允許組件從 Store 中獲取數(shù)據(jù),mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性。
          • Mutation:是唯一更改 store 中狀態(tài)的方法,且必須是同步函數(shù)。
          • Action:用于提交 mutation,而不是直接變更狀態(tài),可以包含任意異步操作。
          • Module:允許將單一的 Store 拆分為多個(gè) store 且同時(shí)保存在單一的狀態(tài)樹中。

          17、使用過 Vue SSR 嗎?說說 SSR?

          ?

          Vue.js 是構(gòu)建客戶端應(yīng)用程序的框架。默認(rèn)情況下,可以在瀏覽器中輸出 Vue 組件,進(jìn)行生成 DOM 和操作 DOM。然而,也可以將同一個(gè)組件渲染為服務(wù)端的 HTML 字符串,將它們直接發(fā)送到瀏覽器,最后將這些靜態(tài)標(biāo)記"激活"為客戶端上完全可交互的應(yīng)用程序。即:SSR大致的意思就是vue在客戶端將標(biāo)簽渲染成的整個(gè) html 片段的工作在服務(wù)端完成,服務(wù)端形成的html 片段直接返回給客戶端這個(gè)過程就叫做服務(wù)端渲染。

          ?

          「服務(wù)端渲染 SSR 的優(yōu)缺點(diǎn)如下:」「(1)服務(wù)端渲染的優(yōu)點(diǎn):」

          • 更好的 SEO:因?yàn)?SPA 頁面的內(nèi)容是通過 Ajax 獲取,而搜索引擎爬取工具并不會(huì)等待 Ajax 異步完成后再抓取頁面內(nèi)容,所以在 SPA 中是抓取不到頁面通過 Ajax 獲取到的內(nèi)容;而 SSR 是直接由服務(wù)端返回已經(jīng)渲染好的頁面(數(shù)據(jù)已經(jīng)包含在頁面中),所以搜索引擎爬取工具可以抓取渲染好的頁面;
          • 更快的內(nèi)容到達(dá)時(shí)間(首屏加載更快):SPA 會(huì)等待所有 Vue 編譯后的 js 文件都下載完成后,才開始進(jìn)行頁面的渲染,文件下載等需要一定的時(shí)間等,所以首屏渲染需要一定的時(shí)間;SSR 直接由服務(wù)端渲染好頁面直接返回顯示,無需等待下載 js 文件及再去渲染等,所以 SSR 有更快的內(nèi)容到達(dá)時(shí)間;

          「(2) 服務(wù)端渲染的缺點(diǎn):」

          • 更多的開發(fā)條件限制:例如服務(wù)端渲染只支持 beforCreate 和 created 兩個(gè)鉤子函數(shù),這會(huì)導(dǎo)致一些外部擴(kuò)展庫需要特殊處理,才能在服務(wù)端渲染應(yīng)用程序中運(yùn)行;并且與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁面應(yīng)用程序 SPA 不同,服務(wù)端渲染應(yīng)用程序,需要處于 Node.js server 運(yùn)行環(huán)境;
          • 更多的服務(wù)器負(fù)載:在 Node.js 中渲染完整的應(yīng)用程序,顯然會(huì)比僅僅提供靜態(tài)文件的 server 更加大量占用CPU 資源 (CPU-intensive - CPU 密集),因此如果你預(yù)料在高流量環(huán)境 ( high traffic ) 下使用,請(qǐng)準(zhǔn)備相應(yīng)的服務(wù)器負(fù)載,并明智地采用緩存策略。

          如果沒有 SSR 開發(fā)經(jīng)驗(yàn)的同學(xué),可以參考本文作者的另一篇 SSR 的實(shí)踐文章《Vue SSR 踩坑之旅》,里面 SSR 項(xiàng)目搭建以及附有項(xiàng)目源碼。

          18、vue-router 路由模式有幾種?

          vue-router 有 3 種路由模式:hash、history、abstract,對(duì)應(yīng)的源碼如下所示:

          switch?(mode)?{
          ??case?'history':
          ?this.history?=?new?HTML5History(this,?options.base)
          ?break
          ??case?'hash':
          ?this.history?=?new?HashHistory(this,?options.base,?this.fallback)
          ?break
          ??case?'abstract':
          ?this.history?=?new?AbstractHistory(this,?options.base)
          ?break
          ??default:
          ?if?(process.env.NODE_ENV?!==?'production')?{
          ???assert(false,?`invalid?mode:?${mode}`)
          ?}
          }
          復(fù)制代碼

          其中,3 種路由模式的說明如下:

          • hash: 使用 URL hash 值來作路由。支持所有瀏覽器,包括不支持 HTML5 History Api 的瀏覽器;

          • history : 依賴 HTML5 History API 和服務(wù)器配置。具體可以查看 HTML5 History 模式;

          • abstract : 支持所有 JavaScript 運(yùn)行環(huán)境,如 Node.js 服務(wù)器端。如果發(fā)現(xiàn)沒有瀏覽器的 API,路由會(huì)自動(dòng)強(qiáng)制進(jìn)入這個(gè)模式.

          19、能說下 vue-router 中常用的 hash 和 history 路由模式實(shí)現(xiàn)原理嗎?

          「(1)hash 模式的實(shí)現(xiàn)原理」早期的前端路由的實(shí)現(xiàn)就是基于 location.hash 來實(shí)現(xiàn)的。其實(shí)現(xiàn)原理很簡單,location.hash 的值就是 URL 中 # 后面的內(nèi)容。比如下面這個(gè)網(wǎng)站,它的 location.hash 的值為 '#search':

          https://www.word.com#search
          復(fù)制代碼

          hash 路由模式的實(shí)現(xiàn)主要是基于下面幾個(gè)特性:

          • URL 中 hash 值只是客戶端的一種狀態(tài),也就是說當(dāng)向服務(wù)器端發(fā)出請(qǐng)求時(shí),hash 部分不會(huì)被發(fā)送;
          • hash 值的改變,都會(huì)在瀏覽器的訪問歷史中增加一個(gè)記錄。因此我們能通過瀏覽器的回退、前進(jìn)按鈕控制hash 的切換;
          • 可以通過 a 標(biāo)簽,并設(shè)置 href 屬性,當(dāng)用戶點(diǎn)擊這個(gè)標(biāo)簽后,URL 的 hash 值會(huì)發(fā)生改變;或者使用 ?JavaScript 來對(duì) loaction.hash 進(jìn)行賦值,改變 URL 的 hash 值;
          • 我們可以使用 hashchange 事件來監(jiān)聽 hash 值的變化,從而對(duì)頁面進(jìn)行跳轉(zhuǎn)(渲染)。

          「(2)history 模式的實(shí)現(xiàn)原理」HTML5 提供了 History API 來實(shí)現(xiàn) URL 的變化。其中做最主要的 API 有以下兩個(gè):history.pushState() 和 history.repalceState()。這兩個(gè) API 可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史紀(jì)錄。唯一不同的是,前者是新增一個(gè)歷史記錄,后者是直接替換當(dāng)前的歷史記錄,如下所示:

          window.history.pushState(null,?null,?path);
          window.history.replaceState(null,?null,?path);
          復(fù)制代碼

          history 路由模式的實(shí)現(xiàn)主要基于存在下面幾個(gè)特性:

          • pushState 和 repalceState 兩個(gè) API 來操作實(shí)現(xiàn) URL 的變化 ;
          • 我們可以使用 popstate 事件來監(jiān)聽 url 的變化,從而對(duì)頁面進(jìn)行跳轉(zhuǎn)(渲染);
          • history.pushState() 或 history.replaceState() 不會(huì)觸發(fā) popstate 事件,這時(shí)我們需要手動(dòng)觸發(fā)頁面跳轉(zhuǎn)(渲染)。

          20、什么是 MVVM?

          Model–View–ViewModel (MVVM) 是一個(gè)軟件架構(gòu)設(shè)計(jì)模式,由微軟 WPF 和 Silverlight 的架構(gòu)師 Ken Cooper 和 Ted Peters 開發(fā),是一種簡化用戶界面的事件驅(qū)動(dòng)編程方式。由 John Gossman(同樣也是 WPF 和 Silverlight 的架構(gòu)師)于2005年在他的博客上發(fā)表 MVVM 源自于經(jīng)典的 Model–View–Controller(MVC)模式 ,MVVM 的出現(xiàn)促進(jìn)了前端開發(fā)與后端業(yè)務(wù)邏輯的分離,極大地提高了前端開發(fā)效率,MVVM 的核心是 ViewModel 層,它就像是一個(gè)中轉(zhuǎn)站(value converter),負(fù)責(zé)轉(zhuǎn)換 Model 中的數(shù)據(jù)對(duì)象來讓數(shù)據(jù)變得更容易管理和使用,該層向上與視圖層進(jìn)行雙向數(shù)據(jù)綁定,向下與 Model 層通過接口請(qǐng)求進(jìn)行數(shù)據(jù)交互,起呈上啟下作用。如下圖所示:

          (1)View 層 View 是視圖層,也就是用戶界面。前端主要由 HTML 和 CSS 來構(gòu)建 。(2)Model 層 Model 是指數(shù)據(jù)模型,泛指后端進(jìn)行的各種業(yè)務(wù)邏輯處理和數(shù)據(jù)操控,對(duì)于前端來說就是后端提供的 api 接口。(3)ViewModel 層 ViewModel 是由前端開發(fā)人員組織生成和維護(hù)的視圖數(shù)據(jù)層。在這一層,前端開發(fā)者對(duì)從后端獲取的 Model 數(shù)據(jù)進(jìn)行轉(zhuǎn)換處理,做二次封裝,以生成符合 View 層使用預(yù)期的視圖數(shù)據(jù)模型。需要注意的是 ViewModel 所封裝出來的數(shù)據(jù)模型包括視圖的狀態(tài)和行為兩部分,而 Model 層的數(shù)據(jù)模型是只包含狀態(tài)的,比如頁面的這一塊展示什么,而頁面加載進(jìn)來時(shí)發(fā)生什么,點(diǎn)擊這一塊發(fā)生什么,這一塊滾動(dòng)時(shí)發(fā)生什么這些都屬于視圖行為(交互),視圖狀態(tài)和行為都封裝在了 ViewModel 里。這樣的封裝使得 ViewModel 可以完整地去描述 View 層。MVVM 框架實(shí)現(xiàn)了雙向綁定,這樣 ViewModel 的內(nèi)容會(huì)實(shí)時(shí)展現(xiàn)在 View 層,前端開發(fā)者再也不必低效又麻煩地通過操縱 DOM 去更新視圖,MVVM 框架已經(jīng)把最臟最累的一塊做好了,我們開發(fā)者只需要處理和維護(hù) ViewModel,更新數(shù)據(jù)視圖就會(huì)自動(dòng)得到相應(yīng)更新。這樣 View 層展現(xiàn)的不是 Model 層的數(shù)據(jù),而是 ViewModel 的數(shù)據(jù),由 ViewModel 負(fù)責(zé)與 Model 層交互,這就完全解耦了 View 層和 Model 層,這個(gè)解耦是至關(guān)重要的,它是前后端分離方案實(shí)施的重要一環(huán)。我們以下通過一個(gè) Vue 實(shí)例來說明 MVVM 的具體實(shí)現(xiàn),有 Vue 開發(fā)經(jīng)驗(yàn)的同學(xué)應(yīng)該一目了然:(1)View 層

          "app">
          ????

          {{message}}


          ????"showMessage()">Click?me

          復(fù)制代碼

          (2)ViewModel 層

          var?app?=?new?Vue({
          ????el:?'#app',
          ????data:?{??//?用于描述視圖狀態(tài)???
          ????????message:?'Hello?Vue!',?
          ????},
          ????methods:?{??//?用于描述視圖行為??
          ????????showMessage(){
          ????????????let?vm?=?this;
          ????????????alert(vm.message);
          ????????}
          ????},
          ????created(){
          ????????let?vm?=?this;
          ????????//?Ajax?獲取?Model?層的數(shù)據(jù)
          ????????ajax({
          ????????????url:?'/your/server/data/api',
          ????????????success(res){
          ????????????????vm.message?=?res;
          ????????????}
          ????????});
          ????}
          })
          復(fù)制代碼

          (3)?Model 層

          {
          ????"url":?"/your/server/data/api",
          ????"res":?{
          ????????"success":?true,
          ????????"name":?"IoveC",
          ????????"domain":?"www.cnblogs.com"
          ????}
          }
          復(fù)制代碼

          21、Vue 是如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定的?

          Vue 數(shù)據(jù)雙向綁定主要是指:數(shù)據(jù)變化更新視圖,視圖變化更新數(shù)據(jù),如下圖所示:

          即:

          其中,View 變化更新 Data ,可以通過事件監(jiān)聽的方式來實(shí)現(xiàn),所以 Vue 的數(shù)據(jù)雙向綁定的工作主要是如何根據(jù) Data 變化更新 View。Vue 主要通過以下 4 個(gè)步驟來實(shí)現(xiàn)數(shù)據(jù)雙向綁定的:實(shí)現(xiàn)一個(gè)監(jiān)聽器 Observer:對(duì)數(shù)據(jù)對(duì)象進(jìn)行遍歷,包括子屬性對(duì)象的屬性,利用 Object.defineProperty() 對(duì)屬性都加上 setter 和 getter。這樣的話,給這個(gè)對(duì)象的某個(gè)值賦值,就會(huì)觸發(fā) setter,那么就能監(jiān)聽到了數(shù)據(jù)變化。實(shí)現(xiàn)一個(gè)解析器 Compile:解析 Vue 模板指令,將模板中的變量都替換成數(shù)據(jù),然后初始化渲染頁面視圖,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù),添加監(jiān)聽數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動(dòng),收到通知,調(diào)用更新函數(shù)進(jìn)行數(shù)據(jù)更新。實(shí)現(xiàn)一個(gè)訂閱者 Watcher:Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁 ,主要的任務(wù)是訂閱 Observer 中的屬性值變化的消息,當(dāng)收到屬性值變化的消息時(shí),觸發(fā)解析器 Compile 中對(duì)應(yīng)的更新函數(shù)。實(shí)現(xiàn)一個(gè)訂閱器 Dep:訂閱器采用 發(fā)布-訂閱 設(shè)計(jì)模式,用來收集訂閱者 Watcher,對(duì)監(jiān)聽器 Observer 和 訂閱者 Watcher 進(jìn)行統(tǒng)一管理。以上四個(gè)步驟的流程圖表示如下,如果有同學(xué)理解不大清晰的,可以查看作者專門介紹數(shù)據(jù)雙向綁定的文章《0 到 1 掌握:Vue 核心之?dāng)?shù)據(jù)雙向綁定》,有進(jìn)行詳細(xì)的講解、以及代碼 demo 示例。

          22、Vue 框架怎么實(shí)現(xiàn)對(duì)象和數(shù)組的監(jiān)聽?

          如果被問到 Vue 怎么實(shí)現(xiàn)數(shù)據(jù)雙向綁定,大家肯定都會(huì)回答 通過 Object.defineProperty() 對(duì)數(shù)據(jù)進(jìn)行劫持,但是 Object.defineProperty() 只能對(duì)屬性進(jìn)行數(shù)據(jù)劫持,不能對(duì)整個(gè)對(duì)象進(jìn)行劫持,同理無法對(duì)數(shù)組進(jìn)行劫持,但是我們?cè)谑褂?Vue 框架中都知道,Vue 能檢測(cè)到對(duì)象和數(shù)組(部分方法的操作)的變化,那它是怎么實(shí)現(xiàn)的呢?我們查看相關(guān)代碼如下:

          /**
          ???*?Observe?a?list?of?Array?items.
          ???*/
          ??observeArray?(items:?Array)?{
          ????for?(let?i?=?0,?l?=?items.length;?i???????observe(items[i])??//?observe?功能為監(jiān)測(cè)數(shù)據(jù)的變化
          ????}
          ??}
          ??/**
          ???*?對(duì)屬性進(jìn)行遞歸遍歷
          ???*/
          ??let?childOb?=?!shallow?&&?observe(val)?//?observe?功能為監(jiān)測(cè)數(shù)據(jù)的變化
          復(fù)制代碼

          通過以上 Vue 源碼部分查看,我們就能知道 Vue 框架是通過遍歷數(shù)組 和遞歸遍歷對(duì)象,從而達(dá)到利用 Object.defineProperty() 也能對(duì)對(duì)象和數(shù)組(部分方法的操作)進(jìn)行監(jiān)聽。

          23、Proxy 與 Object.defineProperty 優(yōu)劣對(duì)比

          「Proxy 的優(yōu)勢(shì)如下:」

          「Object.defineProperty 的優(yōu)勢(shì)如下:」

          24、Vue 怎么用 vm.$set() 解決對(duì)象新增屬性不能響應(yīng)的問題 ?

          受現(xiàn)代 JavaScript 的限制 ,Vue?「無法檢測(cè)到對(duì)象屬性的添加或刪除」。由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)屬性執(zhí)行 getter/setter 轉(zhuǎn)化,所以屬性必須在 data 對(duì)象上存在才能讓 Vue 將它轉(zhuǎn)換為響應(yīng)式的。但是 Vue 提供了?Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)?來實(shí)現(xiàn)為對(duì)象添加響應(yīng)式屬性,那框架本身是如何實(shí)現(xiàn)的呢?我們查看對(duì)應(yīng)的 Vue 源碼:vue/src/core/instance/index.js

          export?function?set?(target:?Array?|?Object,?key:?any,?val:?any):?any?{
          ??//?target?為數(shù)組??
          ??if?(Array.isArray(target)?&&?isValidArrayIndex(key))?{
          ????//?修改數(shù)組的長度,?避免索引>數(shù)組長度導(dǎo)致splcie()執(zhí)行有誤
          ????target.length?=?Math.max(target.length,?key)
          ????//?利用數(shù)組的splice變異方法觸發(fā)響應(yīng)式??
          ????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?本身就不是響應(yīng)式數(shù)據(jù),?直接賦值
          ??if?(!ob)?{
          ????target[key]?=?val
          ????return?val
          ??}
          ??//?對(duì)屬性進(jìn)行響應(yīng)式處理
          ??defineReactive(ob.value,?key,?val)
          ??ob.dep.notify()
          ??return?val
          }
          復(fù)制代碼

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

          25、虛擬 DOM 的優(yōu)缺點(diǎn)?

          「優(yōu)點(diǎn):」

          「缺點(diǎn):」

          26、虛擬 DOM 實(shí)現(xiàn)原理?

          虛擬 DOM 的實(shí)現(xiàn)原理主要包括以下 3 部分:

          如果對(duì)以上 3 個(gè)部分還不是很了解的同學(xué),可以查看本文作者寫的另一篇詳解虛擬 DOM 的文章《深入剖析:Vue核心之虛擬DOM》

          27、Vue 中的 key 有什么作用?

          key 是為 Vue 中 vnode 的唯一標(biāo)記,通過這個(gè) key,我們的 diff 操作可以更準(zhǔn)確、更快速。Vue 的 diff 過程可以概括為:oldCh 和 newCh 各有兩個(gè)頭尾的變量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它們會(huì)新節(jié)點(diǎn)和舊節(jié)點(diǎn)會(huì)進(jìn)行兩兩對(duì)比,即一共有4種比較方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 種比較都沒匹配,如果設(shè)置了key,就會(huì)用 key 再進(jìn)行比較,在比較的過程中,遍歷會(huì)往中間靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一個(gè)已經(jīng)遍歷完了,就會(huì)結(jié)束比較。具體有無 key 的 diff 過程,可以查看作者寫的另一篇詳解虛擬 DOM 的文章《深入剖析:Vue核心之虛擬DOM》 所以 Vue 中 key 的作用是:key 是為 Vue 中 vnode 的唯一標(biāo)記,通過這個(gè) key,我們的 diff 操作可以更準(zhǔn)確、更快速「更準(zhǔn)確」:因?yàn)閹?key 就不是就地復(fù)用了,在 sameNode 函數(shù)?a.key === b.key?對(duì)比中可以避免就地復(fù)用的情況。所以會(huì)更加準(zhǔn)確。「更快速」:利用 key 的唯一性生成 map 對(duì)象來獲取對(duì)應(yīng)節(jié)點(diǎn),比遍歷方式更快,源碼如下:

          function?createKeyToOldIdx?(children,?beginIdx,?endIdx)?{
          ??let?i,?key
          ??const?map?=?{}
          ??for?(i?=?beginIdx;?i?<=?endIdx;?++i)?{
          ????key?=?children[i].key
          ????if?(isDef(key))?map[key]?=?i
          ??}
          ??return?map
          }
          復(fù)制代碼

          28、你有對(duì) Vue 項(xiàng)目進(jìn)行哪些優(yōu)化?

          如果沒有對(duì) Vue 項(xiàng)目沒有進(jìn)行過優(yōu)化總結(jié)的同學(xué),可以參考本文作者的另一篇文章《 Vue 項(xiàng)目性能優(yōu)化 — 實(shí)踐指南 》,文章主要介紹從 3 個(gè)大方面,22 個(gè)小方面詳細(xì)講解如何進(jìn)行 Vue 項(xiàng)目的優(yōu)化。「(1)代碼層面的優(yōu)化」

          「(2)Webpack 層面的優(yōu)化」

          「(3)基礎(chǔ)的 Web 技術(shù)的優(yōu)化」

          29、對(duì)于即將到來的 vue3.0 特性你有什么了解的嗎?

          Vue 3.0 正走在發(fā)布的路上,Vue 3.0 的目標(biāo)是讓 Vue 核心變得更小、更快、更強(qiáng)大,因此 Vue 3.0 增加以下這些新特性:「(1)監(jiān)測(cè)機(jī)制的改變」3.0 將帶來基于代理 Proxy 的 observer 實(shí)現(xiàn),提供全語言覆蓋的反應(yīng)性跟蹤。這消除了 Vue 2 當(dāng)中基于 Object.defineProperty 的實(shí)現(xiàn)所存在的很多限制:

          新的 observer 還提供了以下特性:

          「(2)模板」模板方面沒有大的變更,只改了作用域插槽,2.x 的機(jī)制導(dǎo)致作用域插槽變了,父組件會(huì)重新渲染,而 3.0 把作用域插槽改成了函數(shù)的方式,這樣只會(huì)影響子組件的重新渲染,提升了渲染的性能。同時(shí),對(duì)于 render 函數(shù)的方面,vue3.0 也會(huì)進(jìn)行一系列更改來方便習(xí)慣直接使用 api 來生成 vdom 。「(3)對(duì)象式的組件聲明方式」vue2.x 中的組件是通過聲明的方式傳入一系列 option,和 TypeScript 的結(jié)合需要通過一些裝飾器的方式來做,雖然能實(shí)現(xiàn)功能,但是比較麻煩。3.0 修改了組件的聲明方式,改成了類式的寫法,這樣使得和 TypeScript 的結(jié)合變得很容易。此外,vue 的源碼也改用了 TypeScript 來寫。其實(shí)當(dāng)代碼的功能復(fù)雜之后,必須有一個(gè)靜態(tài)類型系統(tǒng)來做一些輔助管理。現(xiàn)在 vue3.0 也全面改用 TypeScript 來重寫了,更是使得對(duì)外暴露的 api 更容易結(jié)合 TypeScript。靜態(tài)類型系統(tǒng)對(duì)于復(fù)雜代碼的維護(hù)確實(shí)很有必要。「(4)其它方面的更改」vue3.0 的改變是全面的,上面只涉及到主要的 3 個(gè)方面,還有一些其他的更改:

          30、說說你使用 Vue 框架踩過最大的坑是什么?怎么解決的?

          本題為開放題目,歡迎大家在評(píng)論區(qū)暢所欲言,分享自己的踩坑、填坑經(jīng)歷,提供前車之鑒,避免大伙再次踩坑 ~~~


          專注分享當(dāng)下最實(shí)用的前端技術(shù)。關(guān)注前端達(dá)人,與達(dá)人一起學(xué)習(xí)進(jìn)步!

          長按關(guān)注"前端達(dá)人"

          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋中文字幕一区 | 就爱搞AⅤ | 亚洲视频免费观看H | www.日韩一级 | 色色色五月天婷婷 |