1.如何理解MVVM原理
M: Model(模型), vue中是data(為view提供資料)
V: View(檢視), vue中是模板頁面(顯示data中的資料)
VM: ViewModel(檢視模型), vue中是Vue例項物件(管理者: 資料繫結/DOM監聽)
複製程式碼
2.響應式資料的原理是什麼
1、把一個普通的js物件傳給Vue例項的data選項物件
2、Vue將遍歷此物件的所有的屬性,並使用Object.defineProperty把這些屬性全部轉換為getter/setter
3、Vue內部會對資料進行劫持操作,進而追蹤依賴,在屬性被訪問和修改時通知變化
複製程式碼
3.Vue中是如何檢測陣列變化
陣列: 重寫陣列更新陣列元素的方法, 只要呼叫陣列的這些方法, 就會更新相應的介面
物件: 對物件中的屬性進行setter監視, 只要設定了新的屬性值, 就會更新相應的介面
複製程式碼
4.為何Vue採用非同步渲染
因為如果不採用非同步更新,那麼每次更新資料都會對當前元件進行重新渲染,
所以為了效能考慮。Vue會在本輪資料資料更新後,在去更新檢視!
複製程式碼
5.nextTick實現原理
用法:在下次 DOM 更新迴圈結束之後執行延遲迴調。在修改資料之後立即使用這個方法,獲取更新後的 DOM。
原理:vue用非同步佇列的方式來控制DOM更新和nextTick回撥先後執行,每次在回撥佇列的末尾新增一個task
複製程式碼
6.Vue元件的生命週期
利用keep-alive快取了元件之後,再次進入元件不會觸發
beforeCreate、created 、beforeMount、mounted,
如果你想每次進入元件都做一些事情的話,你可以放在activated進入快取元件的鉤子中。
同理:離開快取元件的時候,beforeDestroy和destroyed並不會觸發,
可以使用deactivated離開快取元件的鉤子來代替。
複製程式碼
7.Ajax請求放在哪個生命週期
mounted 在mounted中,資料已經掛載完畢,所以絕對不會多次重複呼叫,這才是ajax合適的位置
如果放在created中,等到非同步渲染開啟的時候,created 就可能被中途打斷,
中斷之後渲染又要重做一遍,在 created 中做ajax呼叫,程式碼裡看到只有呼叫一次,
但是實際上可能呼叫 N 多次,這明顯不合適
複製程式碼
8.何時需要使用beforeDestory
清除定時器,做收尾工作
複製程式碼
9.Vue父子元件生命週期呼叫順序
載入渲染過程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子元件更新過程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父元件更新過程
父 beforeUpdate -> 父 updated
銷燬過程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
複製程式碼
10.Vue中computed的特點
是計算屬性,依賴其它屬性值,並且 computed 的值有快取,
只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值;
computed與watch的差異
computed是計算一個新的屬性,並將該屬性掛載到vm上(Vue例項),
watch是監聽已經存在且已經掛載到vm例項上的資料,所以watch同樣可以監聽computed計算屬性的變化
computed本質上是一個惰性求職的觀察者,具有快取性,只有當依賴變化後,
第一次訪問computed屬性,才會計算新的值,而watch是資料發生變化便會呼叫執行函式
從使用場景來說,computed適用於一個資料被多個資料影響,而watch適用於一個資料影響多個資料
computed實現原理
computed 本質是一個惰性求值的觀察者。
computed內部實現了一個惰性的watcher,也就是computed watcher,computed watcher不會立刻求值,
同時擁有一個dep例項
其內部通過this.dirty屬性標記計算屬性是否需要重新求值
當computed依賴狀態發生改變時,就會通知這個computed watcher
computed watcher通過this.dep.subs.length判斷有沒有訂閱者
如果有,會重新計算,然後對比新舊值,如果值發生變化,就會重新渲染(Vue確保不只是依賴的值發生變化,
而是最終計算的值發生變化,才會重新渲染)
如果沒有,僅僅把this.dirty = true
(當計算屬性依賴於其他資料時,屬性並不會立即重新計算,只有之後需要讀取屬性的時候,才會真正計算,它具備lazy(懶計算特性))
複製程式碼
11.Watch中deep:true是如何實現的
watch 初始化的時候會先讀取一遍監聽資料的值,把資料收集到watch的watcher中,
當資料發生改變時,通知watcher,更新資料,
Vue通過Object.defineProperty給【值是物件的屬性】 設定 set 和 get的時候
如果你直接改變或讀取這個屬性 ( 直接賦值 ),可以觸發這個屬性的設定的 set 和 get
但是你改變或讀取它的內部屬性時,並不會觸發set和get
如果你設定了deep:true,它就會遞迴遍歷這個值(物件),把物件內所有的屬性值收集到watch的watcher中
複製程式碼
12.Vue中資料繫結的原理
資料劫持(監視)和釋出-訂閱模式
a.在observer中, 通過Object.defineProperterty()給data中所有屬性新增setter/getter, 實現資料劫持
b.為每個data中的屬性建立一個對應的dep物件(訂閱器)
c.為模板中的每個表示式建立對應的watcher物件(訂閱者), 並關聯到對應的dep上
d.一旦data中的資料發生變化,setter(釋出者)會通過dep物件通知所有關聯的watcher,
watcher收到通知後就更新對應的節點
複製程式碼
13.Vue中v-html會導致哪些問題
V-html更新的是元素的 innerHTML,內容按普通 HTML 插入,
不會作為Vue模板進行編譯,有的時候我們需要渲染的html片段中有插值表示式,並不會被解析
解決方法: 如果存在事件和插值語法,可以使用元件代替
在單檔案元件裡,scoped 的樣式不會應用在 v-html 內部,因為那部分 HTML 沒有被 Vue 的模板編譯器處理
解決方法: 可以重新定義一個style
複製程式碼
14.Vue中v-if和v-show的區別
v-if 是真正的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子元件適當地被銷燬和重建;
也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。
v-show,就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS的“display”屬性
進行切換。
所以,v-if 適用於在執行時很少改變條件,不需要頻繁切換條件的場景;
v-show 則適用於需要非常頻繁切換條件的場景。
複製程式碼
15.為什麼v-for和v-if不能連用
v-for比v-if優先,如果每一次都需要遍歷整個陣列,將會影響速度,尤其是當之需要渲染很小一部分的時候
可以使用computed屬性,用v-for來迴圈通過computed計算過濾後的物件
複製程式碼
16.v-model中的實現原理及如何自定義v-model
v-model其實是一個語法糖,它會自動的在元素或者元件上面解析為 :value="" 和 @input=""
自定義v-model
一般都是給自己寫的元件做自定義v-model,例如:<my-com v-model="變數"><my-com>
將變數的值與自定義元件中的props: ["value"]定義的value進行資料繫結,props的value獲得了變數值
在自定義元件中通過$emit("input",我們想要傳遞的變數)來手動觸發input事件,
將$emit的第二個引數值傳給<my-com v-model="變數"><my-com>中的變數
複製程式碼
17.元件中的data為什麼是一個函式
因為元件是用來複用的,且 JS 裡物件是引用關係,如果元件中 data 是一個物件,
那麼這樣作用域沒有隔離,子元件中的 data 屬性值會相互影響,如果元件中 data
選項是一個函式,那麼每個例項可以維護一份被返回物件的獨立的拷貝,
元件例項之間的 data 屬性值不會互相影響;而 new Vue 的例項,
是不會被複用的,因此不存在引用物件的問題。
複製程式碼
18.Vue元件如何通訊
1). props
父子元件間通訊的基本方式
屬性值的2大型別:
一般: 父元件-->子元件
函式: 子元件-->父元件
隔層元件間傳遞: 必須逐層傳遞(麻煩)
兄弟元件間: 必須藉助父元件(麻煩)
2). vue自定義事件
方式1: 給子元件標籤繫結事件監聽
子元件向父元件的通訊方式
功能類似於function props
不適合隔層元件和兄弟元件間的通訊
方式2: 通過單獨的vm物件繫結監聽/分發事件
任意元件間通訊
3). 訊息訂閱和釋出(pubsub-js)
適合於任何關係的元件間通訊
缺點: 管理不夠集中
4). vuex
多元件共享狀態(資料的管理)
元件間的關係也沒有限制
功能比pubsub強大, 更適用於vue專案
5). slot
父向子通訊
通訊是帶資料的標籤
注意: 標籤是在父元件中解析
複製程式碼
19.什麼是作用域插槽
父元件在用子元件來填充插槽的時候 有時候需要用到子元件裡面插槽的資料 .子元件檔案插槽上帶的資料
在父元件的子元件標籤裡 讓一個標籤 帶有slot-scope="xxx" 去接收 以便在下面進行呼叫
父元件把資料給子元件,父=>子
子元件把資料給插槽,並暴露給父元件介面
父元件呼叫子元件的插槽slot介面和資料
複製程式碼
20.用vnode來描述一個DOM結構
在vue.js中存在一個VNode類,使用它可以例項化不同型別的vnode例項,
而不同型別的vnode例項各自表示不同型別的DOM元素。
例如,DOM元素有元素節點,文字節點,註釋節點等,vnode例項也會對應著有元素節點和文字節點和註釋節點。
VNode類程式碼如下:
複製程式碼
export default class VNode {
constructor(tag, data, children, text, elm, context, componentOptions, asyncFactory) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.functionalOptions = undefined
this.functionalScopeId = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
get child () {
return this.componentInstance
}
}
複製程式碼
tag 屬性即這個vnode的標籤屬性
data 屬性包含了最後渲染成真實dom節點後,節點上的class,attribute,style以及繫結的事件
children 屬性是vnode的子節點
text 屬性是文字屬性
elm 屬性為這個vnode對應的真實dom節點
key 屬性是vnode的標記,在diff過程中可以提高diff的效率
複製程式碼
21.diff演算法的時間複雜度
diff 演算法用來比較兩棵 Virtual DOM 樹的差異,
如果需要兩棵樹的完全比較,那麼 diff 演算法的時間複雜度為O(n^3)。
但是在前端當中,你很少會跨越層級地移動 DOM 元素,
所以 Virtual DOM 只會對同一個層級的元素進行對比,
如下圖所示, div 只會和同一層級的 div 對比,第二層級的只會跟第二層級對比,
這樣演算法複雜度就可以達到 O(n)。
複製程式碼
22.簡述Vue中diff演算法原理
我們都知道真實dom的開銷是很大的,這個跟效能優化中的重繪意義類似。
某些時候我們修改了頁面中的某個資料,如果直接渲染到真實DOM中會引起整棵樹的重繪,
那麼我們能不能只讓我們改變過的資料對映到真實 DOM,做一個最少的重繪呢,這就是diff演算法要解決的事情
對操作前後的dom樹同一層的節點進行對比,一層一層對比,然後再插入真實的dom中,重新渲染
複製程式碼
23.v-for中為什麼要用key
:key="唯一標識" 唯一標識可以是item裡面id index等,
因為vue元件高度複用增加Key可以標識元件的唯一性,
key的作用主要是為了高效的更新虛擬DOM
複製程式碼
24.描述元件渲染和更新過程
把模板編譯為render函式
例項進行掛載, 根據根節點render函式的呼叫,遞迴的生成虛擬dom
對比虛擬dom,渲染到真實dom
元件內部data發生變化,元件和子元件引用data作為props重新呼叫render函式,生成虛擬dom, 返回到步驟3
複製程式碼
25.Vue中模板編譯原理
1). 目的
實現初始化顯示
2). 整體流程
1. 將el的所有子節點取出, 新增到一個新建的文件fragment物件中
2. 對fragment中的所有層次子節點遞迴進行編譯解析處理
3. 將解析後的fragment新增到el中顯示
3). 編譯/解析包含大括號表示式的文字節點: textNode.textContent = value
4). 編譯事件指令: elementNode.addEventListener('eventName', callback)
5). 編譯一般指令: elementNode.xxx = value
複製程式碼
26.Vue中常見效能優化
v-if 和 v-show 區分使用場景
computed 和 watch 區分使用場景
v-for 遍歷必須為 item 新增 key,且避免同時使用 v-if
長列表效能優化
事件的銷燬
圖片資源懶載入
路由懶載入
第三方外掛的按需引入
複製程式碼
27.Vue中相同邏輯如何抽離
使用mixin,mixin檔案是一個物件,可以包含vue元件的任意成分。
是分發Vue元件可複用功能的非常靈活的方式,
當mixin被元件使用時,所有minxin裡的屬性/方法會與元件裡的屬性/方法混合
在Vue元件中可以有mixins屬性,該屬性值型別為陣列。將mixin引入,
作為mixins陣列的元素mixins: [mixin]
元件A應用了mixin,兩者的屬性如methods,components和directives,將被混合為同一個物件,
如果methods,components和directives中有同名的屬性,則mixin中的將會被忽略。
同名鉤子函式會組成陣列並都會被呼叫,並且mixin的鉤子函式會比元件的鉤子函式先被呼叫。
複製程式碼
28.為什麼要使用非同步元件
在一個 vue 單頁面應用中,隨著應用程式越來越大,功能越來越多,
繼而造成首頁開啟過慢的現象。在這種情況下,可以使用 vue 的非同步元件與路由懶載入來進行首頁開啟速度優化
非同步元件,是指只有頁面需要用到時才從伺服器載入的元件。
複製程式碼
29.談談你對keep-alive的瞭解
keep-alive 是 Vue 內建的一個元件,可以使被包含的元件保留狀態,避免重新渲染
一般結合路由和動態元件一起使用,用於快取元件
當元件在 <keep-alive> 內被切換,
它的 activated 和 deactivated 這兩個生命週期鉤子函式將會被對應執行。
如果一個tab元件,當你點選第一個元件的某個操作時,在切換到第二個元件去,然後在切回第一個元件,
這個時候如果沒有使用keep-alive,那它就會回到元件初始狀態,
如果使用,那他就會把第一個元件進行快取,保留之前的狀態
複製程式碼
30.實現hash路由和history路由
在專案中,我們用histoty路由經常會遇到這樣一個問題,重新整理某個路由路徑時,會出現404的錯誤,
它產生的原因就是專案根路徑的path路徑會被當做後臺路由路徑
它的解決辦法有兩種,一種是當後臺404的時候,讓後臺去處理返回首頁,然後我們就可以當做前臺路由來處理,
另一種是改變history路由為hash路由,這樣請求的路徑都是#號之前的根路徑,
剩下的路由全部由我們前端來處理
hash —— 即位址列 URL 中的 # 符號
比如這個 URL:http://www.123.com/#/hello,hash 的值為 #/hello。
它的特點在於:hash 雖然出現在 URL 中,但不會被包括在 HTTP 請求中,對後端完全沒有影響,
因此改變 hash 不會重新載入頁面。
history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。
這兩個方法應用於瀏覽器的歷史記錄棧,在當前已有的 back、forward、go 的基礎之上,
它們提供了對歷史記錄進行修改的功能。
只是當它們執行修改時,雖然改變了當前的 URL,但瀏覽器不會立即向後端傳送請求
hash 模式和 history 模式都屬於瀏覽器自身的特性,
Vue-Router 只是利用了這兩個特性(通過呼叫瀏覽器提供的介面)來實現前端路由
前端路由中我們主要是利用history.replaceState() history.pushState或者改變hash值不會使頁面重新載入,
然後監聽路由的變化來做到觸發對應時間載入對應資源來重新繪製頁面。
這裡比較重要的點是,監聽hash模式用的是hashchange,history模式用的是popstate
複製程式碼
31.Vue-Route中導航守衛有哪些
全域性守衛
router.beforeEach 全域性前置守衛 進入路由之前
router.beforeResolve 全域性解析守衛(2.5.0+) 在beforeRouteEnter呼叫之後呼叫
router.afterEach 全域性後置鉤子 進入路由之後
路由元件內的守衛
beforeRouteEnter 進入路由前
beforeRouteUpdate (2.2) 路由複用同一個元件時
beforeRouteLeave 離開當前路由時
複製程式碼
32.action和mutation的區別
mutation:專注於修改State,理論上是修改State的唯一途徑,必須同步執行
action:業務程式碼、非同步請求,可以非同步,但不能直接操作State
複製程式碼
33.簡述vuex工作原理
34.Vue3.0你知道有哪些改進
監測機制的改變
3.0 將帶來基於代理 Proxy 的 observer 實現,提供全語言覆蓋的反應性跟蹤。
這消除了 Vue 2 當中基於 Object.defineProperty 的實現所存在的很多限制:
只能監測屬性,不能監測物件
檢測屬性的新增和刪除;
檢測陣列索引和長度的變更;
支援 Map、Set、WeakMap 和 WeakSet。
壓縮包體積更小
當前最小化並被壓縮的 Vue 執行時大小約為 20kB(2.6.10 版為 22.8kB)。
Vue 3.0捆綁包的大小大約會減少一半,即只有10kB!
Virtual DOM 重構複製程式碼