2019自結前端面試小冊

臨近2019年的尾聲,是不是該爲了更好的2020年再戰一回呢? ‘勝敗兵家事不期,包羞忍恥是男兒。江東子弟多才俊,捲土重來未可知’,那些在秋招失利的人,難道就心甘情願放棄嗎!
此文總結2019年以來本人經歷以及瀏覽文章中,較熱門的一些面試題,涵蓋從CSS到JS再到Vue再到網絡等前端基礎到進階的一些知識。
總結面試題涉及的知識點是對自己的一個提升,也希望可以幫助到同學們,在2020年會有一個更好的競爭能力。

css篇 - juejin.im/post/5e040e…
Javavscript篇 - juejin.im/post/5e2122…
ECMAScript 6篇 - juejin.im/post/5e2962…

Module Four - Vue
「About Base」
講一講你對MVVM的理解?與MVC有什麼不同?

MVC指的是Model-View-Controller,即模型-視圖-控制器。

使用MVC的目的就是將模型與視圖分離
MVC屬於單向通信,必須通過Controller來承上啓下,既必須由控制器來獲取數據,將結果返回給前端,頁面重新渲染

MVVM指的是Model-View-ViewModel,即模型-視圖-視圖模型,「模型」指的是後端傳遞的數據,「視圖」指的是所看到的頁面,「視圖模型」是MVVM的核心,它是連接View與Model的橋樑,實現view的變化會自動更新到viewModel中,viewModel中的變化也會自動顯示在view上,是一種數據驅動視圖的模型

區別:

MVC中的Control在MVVM中演變成viewModel
MVVM通過數據來顯示視圖,而不是通過節點操作
MVVM主要解決了MVC中大量的DOM操作,使頁面渲染性能降低,加載速度慢,影響用戶體驗的問題

請說一下Vue響應式數據的原理?
Vue底層對於響應式數據的核心是object.defineProperty,Vue在初始化數據時,會給data中的屬性使用object.defineProperty重新定義屬性(劫持屬性的getter和setter),當頁面使用對應屬性時,會進行依賴收集(收集當前組件的watcher),如果屬性發生變化,會通知相關依賴進行更新操作

總結:
Vue通過數據劫持配合發佈者-訂閱者的設計模式,內部通過調用object.defineProperty()來劫持各個屬性的getter和setter,在數據變化的時候通知訂閱者,並觸發相應的回調

講一講Vue是如何檢測數組的變化?

核心思想:使用了函數劫持的方式,重寫了數組的方法(push,pop,unshift,shift···)
Vue將data中的數組,進行了原型鏈的重寫,指向了自己所定義的數組原型方法,當調用數組的API時,可以通知依賴更新,如果數組中包含着引用類型,會對數組中的引用類型再次進行監控

爲什麼Vue要採取異步渲染?
因爲如果不採用異步渲染,那麼每次更新數據都會進行重新渲染,爲了提高性能,Vue通過異步渲染的方式,在本輪數據更新後,再去異步更新視圖

「譯」Object.defineProperty有什麼缺點?(爲什麼Vue3.0開始使用Proxy實現響應式)

Object.defineProperty只能劫持對象的屬性,因此需要遍歷對象的每個屬性,而Proxy可以直接代理對象
Object.defineProperty對新增屬性需要手動進行觀察,由於Object.defineProperty劫持的是對象的屬性(第一點),所以新增屬性時,需要重新遍歷對象,對其新增屬性再使用Object.defineProperty進行劫持 (正是這個原因導致我們在給data中的數組或對象新增屬性時,需要使用$set才能保證視圖可以更新)
Proxy性能高,支持13種攔截方式

nextTick實現原理是什麼? 在Vue中有什麼作用

原理:EventLoop事件循環
作用:在下次dom更新循環結束後執行延遲迴調,當我們修改數據之後立即使用nextTick()來獲取最新更新的Dom

watch中的deep:true是如何實現的?
當用戶指定了watch中的deep:true時,如果當前監控的值是數組類型(對象類型),會對對象中的每一項進行求值,此時會將當前watcher存入到對應屬性的依賴中,這樣數組中的對象發生變化也會通知數據進行更新
缺點:由於需要對每一項都進行操作,性能會降低,不建議多次使用deep:true

爲什麼v-if與v-for不建議連在一起使用?
v-for優先級高於v-if,如果連在一起使用的話會把v-if給每一個元素都添加上,重複運行於每一個v-for循環中,會造成性能浪費

「譯」組件的data爲什麼要寫成函數形式
在Vue中,組件都是可複用的,一個組件創建好後,可以在多個地方重複使用,而不管複用多少次,組件內的data都必須是相互隔離,互不影響的,如果data以對象的形式存在,由於Javascript中對象是引用類型,作用域沒有隔離,因此data必須以函數的形式返回

總結:爲了實現每個組件實例可以維護獨立的數據拷貝,不會相互影響

❗ 小知識: new Vue根組件不需要複用,因此不需要以函數方式返回

「譯」v-for中的key的作用是什麼?
key是爲每個vnode指定唯一的id,在同級vnode的Diff過程中,可以根據key快速的進行對比,來判斷是否爲相同節點,並利用key的唯一性生成map來更快的獲取相應的節點,另外指定key後,可以保證渲染的準確性

Vue每個生命週期什麼時候被調用?

beforeCreate → 在實例初始化之後,數據觀測(data observer)之前被調用
created → 實例已經創建完成之後被調用。在這裏,實例已完成以下配置:

數據觀測(data observer)
屬性和方法的運算
watch/event事件回調
但這裏還沒有$el

beforeMount → 在掛載開始之前被調用,相關的render函數首次被調用
mounted → elvm.el被新創建的vm.el替換,並掛載到實例上之後調用該鉤子
beforeUpdate → 數據更新時調用,發生在虛擬DOM重新渲染和打補丁之前
updated → 由於數據更改導致的虛擬DOM重新渲染和打補丁,在這之後會調用該鉤子(該鉤子在服務器端渲染期間不被調用)
beforeDestroy → 實例銷燬之前調用,在這裏,實例仍然完全可以使用
destroyed → Vue實例銷燬後調用。調用該鉤子後,Vue實例指示的所有東西都會解綁,所有的事件監聽器會被移除,所有的子實例也會被銷燬(該鉤子在服務器端渲染期間不被調用)

Vue每個生命週期內部可以做什麼事?

created → 實例已經創建完成,由於它是最早觸發的,所以可以進行一些數據,資源的請求
mounted → 實例已經掛載完成,可以進行一些DOM操作
beforeUpdate → 可以在該鉤子中進一步地更改狀態,這不會觸發附加的渲染過程
updated → 可以執行依賴於DOM的操作。但在大多數情況下,應避免在該鉤子中更改狀態,因爲這可能導致更新無限循環
destroyed → 可以執行一些優化操作,例如清空定時器,清理緩存,解除事件綁定等

Vue組件有哪些聲明生命週期鉤子?
「beforeCreate」、「created」、「beforeMount」、「mounted」、「beforeUpdate」、「updated」、「beforeDestroy」、「destroyed」
❗ 小知識:
擁有自己獨立的鉤子函數 activated | deactivated

activated → 在被包裹的組件中才有效,當組件被激活時使用該鉤子
deactivated → 在被包裹的組件中才有效,當組件被停止時使用該鉤子

「譯」Vue的父組件和子組件生命週期鉤子執行順序是什麼?

理解渲染過程:

父組件掛載完成必須是等到子組件都掛載完成之後,纔算父組件掛載完,所以父組件的mounted肯定是在子組件mounted之後

So:「父」beforeCreate → 「父」created → 「父」beforeMount → 「子」beforeCreate → 「子」created → 「子」beforeMount → 「子」mounted → 「父」mounted

子組件更新過程(取決於對父組件是否有影響)

影響到父組件: 「父」beforeUpdate → 「子」beforeUpdate → 「子」updated → 「父」updated
不影響父組件: 「子」beforeUpdate → 「子」updated

父組件更新過程(取決於對子組件是否有影響)

影響到子組件: 「父」beforeUpdate → 「子」beforeUpdate → 「子」updated → 「父」updated
不影響子組件: 「父」beforeUpdate → 「父」updated

銷燬過程

「父」beforeDestroy → 「子」beforeDestroy → 「子」destroyed → 「父」destroyed

怎麼理解vue的單向數據流

在vue中,父組件可以通過prop將數據傳遞給子組件,但這個prop只能由父組件來修改,子組件修改的話會拋出錯誤
如果是子組件想要修改數據,只能通過$emit由子組件派發事件,並由父組件接收事件進行修改

爲什麼子組件不可以修改父組件傳遞的Prop?(爲什麼vue提倡單向數據流)
由於vue提倡單向數據流,即父級props的更新會流向子組件,但反過來則不行。這是爲了防止意外的改變父組件的狀態,使得應用的數據流變得難以理解。如果破壞了單項數據流,當應用複雜時,debug的成本將會非常高

組件間有哪些通信方式?

父子組件通信

props / event
$parent / $children
ref
provide / inject
.sync

非父子組件通信

eventBus
通過根實例$root
vuex
$attr / $listeners
provide / inject

❗ 小知識: 關於.sync的使用
假設有一個組件 comp

傳遞foo值並用sync修飾,會被擴展成

<comp :foo=“bar” @update:foo=“val => bar = val”>
複製代碼當子組件comp需要更新foo的值時,它需要顯示地觸發一個更新事件

this.$emit(‘update:foo’, newValue)
複製代碼
說一說vue的動態組件

多個組件通過同一個掛載點進行組件的切換,is的值是哪個組件的名稱,那麼頁面就會顯示哪個組件

複製代碼 講一講遞歸組件的用法

組件是可以在它們自己的模板中調用自身的,不過它們只能通過name選項來做這件事

首先我們要知道,既然是遞歸組件,那麼一定要有一個結束的條件,否則就會導致組件無限循環使用,最終出現max stack size exceeded的錯誤,也就是棧溢出。所以,我們應該使用v-if = 'false’來作爲遞歸組件的結束條件,當遇到v-if = 'false’時,組件將不會再進行渲染

自定義組件的語法糖v-model是怎麼樣實現的?(v-model如何實現雙向綁定)
v-model本質是v-bind和v-on的語法糖,用來在表單控件或組件上創建雙向綁定。

等價於


複製代碼在一個組件上使用v-model,默認會爲組件綁定名爲value的prop和名爲input的事件

「譯」Vuex和單純的全局對象有什麼區別?
vuex和全局對象主要有兩大區別:

vuex的狀態存儲是響應式的。當vue組件從store中讀取狀態時,若store中的狀態發生變化,那麼相應的組件也會得到高效更新
不呢能直接改變store中的狀態,改變store中的狀態唯一方法是顯示地提交mutation(commit)。這樣使得我們可以方便地跟蹤每一個狀態的變化

「譯」爲什麼vuex的mutation中不能做異步操作?
vuex中所有的狀態更新的唯一方式都是提交mutation,異步操作需要通過action來提交mutation(dispatch)。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地使用vuex
每個mutation執行完後都會對應得到一個新的狀態變更,這樣devtools就可以打個快照存下來,然後就可以實現time-travel了。
如果mutation支持異步操作,就沒有辦法知道狀態是何時更新,無法很好的進行狀態追蹤,影響調試效率

v-show / v-if 的區別是什麼?

v-if指如果條件不成立則不會渲染當前指令所在節點的Dom元素,會在切換過程中對條件塊的事件監聽器和子組件進行銷燬和重建
v-show只是基於css進行切換,不管條件是什麼,都會進行渲染(切換display:block | none)

So:v-if切換的開銷較大,而v-show初始化的開銷較大,所以在需要頻繁切換顯示和隱藏的Dom元素時,使用v-show更合適,渲染後很少進行切換則使用v-if較合適

computed / watch 的區別是什麼?

computed是依賴於其他屬性的一個計算值,並且具備緩存,只有當依賴的值發生變化纔會更新(自動監聽依賴值的變化,從而動態返回內容)
watch是在監聽的屬性發生變化的時候,觸發一個回調,在回調中執行一些邏輯

So:computed和watch區別在於用法上的不同,computed適合在模板渲染中,如果是需要通過依賴來獲取動態值,就可以使用計算屬性。而如果是想在監聽值變化時執行業務邏輯,就使用watch

computed / methods 的區別是什麼?

computed是基於它們響應式依賴進行緩存,只有在依賴值發生變化,纔會進行計算求值
methods每次使用都會執行相應的方法

Vue中 v-html 有什麼作用?會導致什麼問題?
v-html可以用來識別HTML標籤並渲染出去
導致問題: 在網站上動態渲染任意Html,很容易導致受到Xss攻擊,所以只能在可信內容上使用v-html,且永遠不能用於用戶提交的內容上

keep-alive在vue中的作用是什麼?
包裹在裏組件,在切換時會保存其組件的狀態,使其不被銷燬,防止多次渲染,
· keep-alive擁有兩個獨立的生命週期(activated | deactivated),使keep-alive包裹的組件在切換時不被銷燬,而是緩存到內存中並執行deactivated鉤子,切換回組件時會獲取內存,渲染後執行activated鉤子

如何新增自定義指令?

創建局部指令

var app = new Vue({
el: ‘#app’,
data: {
},
// 創建指令(可以多個)
directives: {
// 指令名稱
dir1: {
inserted(el) {
// toDo
}
}
}
})
複製代碼
創建全局指令

Vue.directive(‘dir2’, {
inserted(el) {
// inserted 表示元素插入時
// toDo
}
})
複製代碼
指令使用

複製代碼 如何自定義過濾器

創建局部過濾器

var app = new Vue({
el: ‘#app’,
data: {
},
// 創建指令(可以多個)
filters: {
// 指令名稱
newfilter:function(value){
// toDo
}
}
})
複製代碼
創建全局過濾器

Vue.filter(‘newfilter’, function (value) {
// toDo
})
複製代碼
過濾器使用

{{xxx | newfilter}}
複製代碼 vue常用修飾符有哪些,有什麼用?

.prevent :攔截默認事件
.passive :不攔截默認事件
.stop :阻止事件冒泡
.self :當事件發生在該元素而不是子元素的時候會觸發
.capture :事件偵聽,事件發生的時候會調用

vue等單頁面應用優缺點是什麼?

優點:vue的目的是通過儘可能簡單的API實現相應的數據綁定和組合的視圖組件,核心是一個響應的數據綁定系統。MVVM,數據驅動,組件化,輕量,簡潔,高效,快速。
缺點

不支持低版本的瀏覽器,最低只支持IE9
不利於SEO的優化
第一次加載首頁耗時相對長一些,不可以使用瀏覽器的導航按鈕需要自行實現前進和後退

如何讓css僅在當前組件中起作用

「About Router」
關於路由,route / router 有什麼區別?

route 表示路由信息對象,包括path,params,hash,query,fullPath,matched,name等路由信息參數
router 表示路由實例對象,包括了路由的跳轉方法,鉤子函數等

「譯」 vue-Router中有哪些導航守衛

「全局前置鉤子」:beforeEach,beforeResolve,afterEach
「路由獨享守衛」:beforeEnter
「組件內部守衛」:beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave

導航解析流程:

導航被觸發
在失活的組件裏調用beforeRouteLeave離開守衛
調用全局的beforeEach守衛
在重用的組件裏調用beforeRouteUpdate守衛
在路由配置裏調用beforeEnter守衛
解析異步路由組件
在被激活的組件裏調用beforeRouteEnter守衛
調用全局的beforeResolve守衛
導航被確認
調用全局的afterEach守衛
觸發Dom更新
用創建好的實例調用beforeRouteEnter守衛中傳給next的回調

「譯」vue-Router 中 hash / history 兩種模式有什麼區別?

hash模式會在url上顯示’#’,而history模式沒有
刷新頁面時,hash模式可以正常加載到hash值對應的頁面,history模式沒有處理的話,會返回404,一般需要後端將所有頁面都配置重定向到首頁路由
兼容性上,hash模式可以支持低版本瀏覽器和IE

「譯」 vue-router 中 hash / history 是如何實現的?

hash模式

#後面hash值的變化,不會導致瀏覽器向服務器發出請求,瀏覽器不發出請求,就不會刷新頁面,同時通過監聽hashchange事件可以知道hash發生了哪些變化。根據hash變化來實現頁面的局部更新

history模式

history模式的實現,主要是Html5標準發佈的兩個Api(pushState和replaceState),這兩個Api可以改變url,但是不會發送請求,這樣就可以監聽url的變化來實現局部更新

怎麼定義 vue-router 的動態路由?怎麼獲取傳過來的值

動態路由的創建,主要是使用path屬性過程中,使用動態路徑參數,以冒號開頭

{
path:’/details/:id’,
name:‘Details’,
components:Details
}

訪問details前綴下的路徑,例如details/1details/2等,都會映射到Details這個組件

複製代碼
當匹配到/details下的路由時,參數值會被設置到this.$route.params下,所以通過這個屬性可以獲取動態參數

this.$route.params.id
複製代碼
vue-router 傳參方式有哪些?

通過params

只能用name,不能用path
參數不會顯示在url上
瀏覽器強制刷新會清空參數

通過query

只能用path,不能用name
name可以使用path路徑
參數會顯示在url上
瀏覽器刷新不清空參數

「About Vuex」

vuex有什麼優缺點?

優點

解決了非父子組件的消息傳遞(將數據存放在state中)
減少了Ajax請求次數,有些情景可以直接從內存中的State獲取

缺點

刷新瀏覽器,vuex中的State就會重新變回初始化狀態

vuex有哪幾種屬性?

State :vuex的基本數據,用來存儲變量
Getter :從基本數據state派生的數據,相當於state的計算屬性
Mutation :提交更新數據的方法,必須是同步的(需要異步則使用action)。每個mutation都有一個字符串的事件類型(type)和一個回調函數(handler)
Action :和mutation的功能大致相同,不同在於

action提交的是mutation,而不是直接變更狀態
action可以包含任意異步操作

Module :模塊化vuex,可以讓每一個模塊擁有自己的state,mutation,action,getter,使得結構清晰,方便管理

vuex 中的 state 有什麼特性?

vuex就是一個倉庫,倉庫裏面放了很多對象,其中state就是數據源存放地
state裏面存放的數據是響應式的,Vue組件從store中讀取數據,若是store中的數據改變,依賴這個數據的組件也會更新數據
它通過mapState把全局的state和getters映射到當前組件的computed計算屬性中

vuex 中的 getters 有什麼特性?

getters可以對state進行計算操作,可以把它看做store的computed計算屬性
雖然在組件中也可以做計算屬性,但getters可以在多個組件之間複用
如果一個狀態只在一個組件內使用,是可以不用getters

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