vue 面試題總結
1. vue和react的差別
- => 相同點:
1. 資料驅動頁面,提供響應式的試圖元件 2. 都有virtual DOM,元件化的開發,通過props參數進行父子之間元件傳遞資料,都實作了webComponents規範 3. 資料流動單向,都支援伺服器的渲染SSR 4. 都有支援native的方法,react有React native, vue有wexx 複制代碼
- => 不同點:
1.資料綁定:Vue實作了雙向的資料綁定,react資料流動是單向的 2.資料渲染:大規模的資料渲染,react更快 3.使用場景:React配合Redux架構适合大規模多人協作複雜項目,Vue适合小快的項目 4.開發風格:react推薦做法jsx + inline style把html和css都寫在js了 vue是采用webpack + vue-loader單檔案元件格式,html, js, css同一個檔案 複制代碼
2. 介紹下MVVM(資料的雙向綁定
- M: model資料模型
- V: view 界面
- MV:作為橋梁負責溝通view跟model
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SZ0EzMzImZjljY3Q2N0QWZ4YTN5U2NxMmM2UjY5YmYw8CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- 隻關心資料的流傳,減少強耦合性。最關鍵的就是資料的雙向綁定
關鍵步驟: 1.實作資料監聽器Observer,用object.defineProperty()重寫資料的get/set。 值更新就在set中通知訂閱者更新資料 2.實作模闆編譯compile,深度周遊dom樹,對每個元素節點的指令模闆替換資料以及訂閱資料 3.實作watch用于連接配接Observer和compile,能夠訂閱并接受每一個屬性的變動的通知, 執行指令綁定的相應的回調函數,進而更新資料 4.實作一個訂閱器 Dep:訂閱器采用 釋出-訂閱 設計模式, 用來收集訂閱者 Watcher,對監聽器 Observer 和 訂閱者 Watcher 進行統一管理。 複制代碼
- mvc和mvvm其實差別并不大。都是一種設計思想。主要就是mvc中Controller演變成mvvm中的viewModel。mvvm主要解決了mvc中大量的DOM 操作使頁面渲染性能降低, 加載速度變慢,影響使用者體驗。和當 Model 頻繁發生變化,開發者需要主動更新到View。
3. 生命周期函數
new Vue(建立一個Vue對象)--> beforeCreate --> observer Data(開始監控data對象資料變化) -->
init event(vue内部初始化事件)
--> created() --> compile(編譯模闆,把data裡面的資料和模闆生成html) -->
beforeMount(還沒有生成HTML到元素上) -->
mounted(挂載完成,也就是模闆中的html渲染到了html頁面中) --> beforeUpdate (Vritual Dom) -->
updated --> beforeDestroy --> destroyed
複制代碼
- 1.ajax請求最好放在created裡面,頁面可以通路到this了
- 2.關于dom的操作要放在mounted裡面,在mounted前面還沒有生成dom
- 3.每次進入/離開元件都要做一些事情,用什麼鈎子函數:
- 不緩存:進入的時候可以用created和mounted鈎子,離開的時候可以使用beforedDestory(可以通路this)和destoryed
- 緩存:緩存了元件之後,在次進入元件不會觸發beforeCreate,created, beforeMount,mounted 如果你想每次進入元件都做一些事情的話,你可以放在activated進入緩存元件的鈎子中
4. 說說你對 SPA 單頁面的了解,它的優缺點分别是什麼?
- SPA( single-page application )僅在 Web 頁面初始化時加載相應的 HTML、JavaScript 和 CSS。一旦頁面加載完成,SPA 不會因為使用者的操作而進行頁面的重新加載或跳轉;取而代之的是利用路由機制實作 HTML 内容的變換,UI 與使用者的互動,避免頁面的重新加載。
- 優點:
- 使用者體驗好、快,内容的改變不需要重新加載整個頁面,避免了不必要的跳轉和重複渲染;基于上面一點,SPA 相對對伺服器壓力小;前後端職責分離,架構清晰,前端進行互動邏輯,後端負責資料處理;
- 缺點:
- 初次加載耗時多:為實作單頁 Web 應用功能及顯示效果,需要在加載頁面的時候将 JavaScript、CSS 統一加載,部分頁面按需加載;前進後退路由管理:由于單頁應用在一個頁面中顯示所有的内容,是以不能使用浏覽器的前進後退功能,所有的頁面切換需要自己建立堆棧管理;SEO 難度較大:由于所有的内容都在一個頁面中動态替換顯示,是以在 SEO 上其有着天然的弱勢。
5. Vue的SPA 如何優化加載速度
1.減少入口檔案體積
2.靜态資源本地緩存
3.開啟Gzip壓縮
4.使用SSR,nuxt.js
複制代碼
6. v-show 與 v-if 有什麼差別?
- v-if 是真正的條件渲染,因為它會確定在切換過程中條件塊内的事件監聽器和子元件适當地被銷毀和重建;也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。
- v-show 就簡單得多——不管初始條件是什麼,元素總是會被渲染,并且隻是簡單地基于 CSS 的 “display” 屬性進行切換。
- 是以,v-if 适用于在運作時很少改變條件,不需要頻繁切換條件的場景;v-show 則适用于需要非常頻繁切換條件的場景。
7. 怎樣了解 Vue 的單向資料流?
所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子元件中,但是反過來則不行。這樣會防止從子元件意外改變父級元件的狀态,進而導緻你的應用的資料流向難以了解。
額外的,每次父級元件發生更新時,子元件中所有的 prop 都将會重新整理為最新的值。這意味着你不應該在一個子元件内部改變 prop。如果你這樣做了,Vue 會在浏覽器的控制台中發出警告。子元件想修改時,隻能通過 $emit 派發一個自定義事件,父元件接收到後,由父元件修改。
有兩種常見的試圖改變一個 prop 的情形 :
- 這個 prop 用來傳遞一個初始值;這個子元件接下來希望将其作為一個本地的 prop 資料來使用。在這種情況下,最好定義一個本地的 data 屬性并将這個 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
複制代碼
- 這個 prop 以一種原始的值傳入且需要進行轉換。在這種情況下,最好使用這個 prop 的值來定義一個計算屬性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
複制代碼
8. computed 和 watch 的差別和運用的場景?
- computed:是計算屬性,依賴其它屬性值,并且 computed 的值有緩存,隻有它依賴的屬性值發生改變,下一次擷取 computed 的值時才會重新計算 computed 的值;
- watch:更多的是「觀察」的作用,類似于某些資料的監聽回調 ,每當監聽的資料變化時都會執行回調進行後續操作;
運用場景:
當我們需要進行數值計算,并且依賴于其它資料時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次擷取值時,都要重新計算;
當我們需要在資料變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行異步操作 ( 通路一個 API ),限制我們執行該操作的頻率,并在我們得到最終結果前,設定中間狀态。這些都是計算屬性無法做到的。
9. 直接給一個數組項指派,Vue 能檢測到變化嗎?
由于 JavaScript 的限制,Vue 不能檢測到以下數組的變動:
- 當你利用索引直接設定一個數組項時,例如:
vm.items[indexOfItem] = newValue
- 當你修改數組的長度時,例如:
vm.items.length = newLength
為了解決第一個問題,Vue 提供了以下操作方法:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一個别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
複制代碼
為了解決第二個問題,Vue 提供了以下操作方法:
// Array.prototype.splice
vm.items.splice(newLength)
複制代碼
10. Vue 的父元件和子元件生命周期鈎子函數執行順序?
Vue 的父元件和子元件生命周期鈎子函數執行順序可以歸類為以下 4 部分:
- 加載渲染過程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
- 子元件更新過程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
- 父元件更新過程 父 beforeUpdate -> 父 updated
- 銷毀過程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
11. 在哪個生命周期内調用異步請求?
可以在鈎子函數 created、beforeMount、mounted 中進行調用,因為在這三個鈎子函數中,data 已經建立,可以将服務端端傳回的資料進行指派。但是本人推薦在 created 鈎子函數中調用異步請求,因為在 created 鈎子函數中調用異步請求有以下優點:
- 能更快擷取到服務端資料,減少頁面 loading 時間;
- ssr 不支援 beforeMount 、mounted 鈎子函數,是以放在 created 中有助于一緻性;
12. 在什麼階段才能通路操作DOM?
在鈎子函數 mounted 被調用前,Vue 已經将編譯好的模闆挂載到頁面上,是以在 mounted 中可以通路操作 DOM。vue 具體的生命周期示意圖可以參見如下,了解了整個生命周期各個階段的操作,關于生命周期相關的面試題就難不倒你了。
13. 父元件可以監聽到子元件的生命周期嗎?
比如有父元件 Parent 和子元件 Child,如果父元件監聽到子元件挂載 mounted 就做一些邏輯處理,可以通過以下寫法實作:
// Parent.vue
// Child.vue
mounted() {
this.$emit("mounted");
}
複制代碼
以上需要手動通過 $emit 觸發父元件的事件,更簡單的方式可以在父元件引用子元件時通過 @hook 來監聽即可,如下所示:
// Parent.vue
doSomething() {
console.log('父元件監聽到 mounted 鈎子函數 ...');
},
// Child.vue
mounted(){
console.log('子元件觸發 mounted 鈎子函數 ...');
},
// 以上輸出順序為:
// 子元件觸發 mounted 鈎子函數 ...
// 父元件監聽到 mounted 鈎子函數 ...
複制代碼
當然 @hook 方法不僅僅是可以監聽 mounted,其它的生命周期事件,例如:created,updated 等都可以監聽。
14. 為什麼元件中的 data 必須是一個函數,然後 return 一個對象,而 new Vue 執行個體裡,data 可以直接是一個對象?
// data
data() {
return {
message: "子元件",
childName:this.name
}
}
// new Vue
new Vue({
el: '#app',
router,
template: '',
components: {App}
})
複制代碼
因為元件是用來複用的,且 JS 裡對象是引用關系,如果元件中 data 是一個對象,那麼這樣作用域沒有隔離,子元件中的 data 屬性值會互相影響,如果元件中 data 選項是一個函數,那麼每個執行個體可以維護一份被傳回對象的獨立的拷貝,元件執行個體之間的 data 屬性值不會互相影響;而 new Vue 的執行個體,是不會被複用的,是以不存在引用對象的問題。
15. v-model 的原理?
我們在 vue 項目中主要使用 v-model 指令在表單 input、textarea、select 等元素上建立雙向資料綁定,我們知道 v-model 本質上不過是文法糖,v-model 在内部為不同的輸入元素使用不同的屬性并抛出不同的事件:
- text 和 textarea 元素使用 value 屬性和 input 事件;
- checkbox 和 radio 使用 checked 屬性和 change 事件;
- select 字段将 value 作為 prop 并将 change 作為事件。
以 input 表單元素為例:
相當于
複制代碼
如果在自定義元件中,v-model 預設會利用名為 value 的 prop 和名為 input 的事件,如下所示:
{{value}}
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小紅')
},
},
複制代碼
16. Vue 元件間通信有哪幾種方式?
Vue 元件間通信是面試常考的知識點之一,這題有點類似于開放題,你回答出越多方法當然越加分,表明你對 Vue 掌握的越熟練。Vue 元件間通信隻要指以下 3 類通信:父子元件通信、隔代元件通信、兄弟元件通信,下面我們分别介紹每種通信方式且會說明此種方法可适用于哪類元件間通信。
(1)
props / $emit
适用 父子元件通信 這種方法是 Vue 元件的基礎,相信大部分同學耳聞能詳,是以此處就不舉例展開介紹。
(2)
ref 與 $parent / $children
适用 父子元件通信
-
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子元件上,引用就指向元件執行個體ref
-
:通路父 / 子執行個體$parent / $children
(3)
EventBus ($emit / $on)
适用于 父子、隔代、兄弟元件通信 這種方法通過一個空的 Vue 執行個體作為中央事件總線(事件中心),用它來觸發事件和監聽事件,進而實作任何元件間的通信,包括父子、隔代、兄弟元件。
(4)
$attrs/$listeners
适用于 隔代元件通信
-
:包含了父作用域中不被 prop 所識别 (且擷取) 的特性綁定 ( class 和 style 除外 )。當一個元件沒有聲明任何$attrs
時,這裡會包含所有父作用域的綁定 ( class 和 style 除外 ),并且可以通過prop
傳入内部元件。通常配合v-bind="$attrs"
選項一起使用。inheritAttrs
-
:包含了父作用域中的 (不含 .native 修飾器的)$listeners
事件監聽器。它可以通過v-on
傳入内部元件v-on="$listeners"
(5)
provide / inject
适用于 隔代元件通信 祖先元件中通過
provider
來提供變量,然後在子孫元件中通過
inject
來注入變量。
provide / inject API
主要解決了跨級元件間的通信問題,不過它的使用場景,主要是子元件擷取上級元件的狀态,跨級元件間建立了一種主動提供與依賴注入的關系。(6)
Vuex
适用于 父子、隔代、兄弟元件通信 Vuex 是一個專為 Vue.js 應用程式開發的狀态管理模式。每一個 Vuex 應用的核心就是 store(倉庫)。“store” 基本上就是一個容器,它包含着你的應用中大部分的狀态 ( state )。
- Vuex 的狀态存儲是響應式的。當 Vue 元件從 store 中讀取狀态的時候,若 store 中的狀态發生變化,那麼相應的元件也會相應地得到高效更新。
- 改變 store 中的狀态的唯一途徑就是顯式地送出 (commit) mutation。這樣使得我們可以友善地跟蹤每一個狀态的變化。
17. 你使用過 Vuex 嗎?
Vuex 是一個專為 Vue.js 應用程式開發的狀态管理模式。每一個 Vuex 應用的核心就是 store(倉庫)。“store” 基本上就是一個容器,它包含着你的應用中大部分的狀态 ( state )。
(1)Vuex 的狀态存儲是響應式的。當 Vue 元件從 store 中讀取狀态的時候,若 store 中的狀态發生變化,那麼相應的元件也會相應地得到高效更新。
(2)改變 store 中的狀态的唯一途徑就是顯式地送出 (commit) mutation。這樣使得我們可以友善地跟蹤每一個狀态的變化。
主要包括以下幾個子產品:
- State:定義了應用狀态的資料結構,可以在這裡設定預設的初始狀态。
- Getter:允許元件從 Store 中擷取資料,mapGetters 輔助函數僅僅是将 store 中的 getter 映射到局部計算屬性。
- Mutation:是唯一更改 store 中狀态的方法,且必須是同步函數。
- Action:用于送出 mutation,而不是直接變更狀态,可以包含任意異步操作。
- Module:允許将單一的 Store 拆分為多個 store 且同時儲存在單一的狀态樹中。
18. 使用過 Vue SSR 嗎?說說 SSR?
Vue.js 是建構用戶端應用程式的架構。預設情況下,可以在浏覽器中輸出 Vue 元件,進行生成 DOM 和操作 DOM。然而,也可以将同一個元件渲染為服務端的 HTML 字元串,将它們直接發送到浏覽器,最後将這些靜态标記"激活"為用戶端上完全可互動的應用程式。即:SSR大緻的意思就是vue在用戶端将标簽渲染成的整個 html 片段的工作在服務端完成,服務端形成的html 片段直接傳回給用戶端這個過程就叫做服務端渲染。
服務端渲染 SSR 的優缺點如下:
(1)服務端渲染的優點:
- 更好的 SEO:因為 SPA 頁面的内容是通過 Ajax 擷取,而搜尋引擎爬取工具并不會等待 Ajax 異步完成後再抓取頁面内容,是以在 SPA 中是抓取不到頁面通過 Ajax 擷取到的内容;而 SSR 是直接由服務端傳回已經渲染好的頁面(資料已經包含在頁面中),是以搜尋引擎爬取工具可以抓取渲染好的頁面;
- 更快的内容到達時間(首屏加載更快):SPA 會等待所有 Vue 編譯後的 js 檔案都下載下傳完成後,才開始進行頁面的渲染,檔案下載下傳等需要一定的時間等,是以首屏渲染需要一定的時間;SSR 直接由服務端渲染好頁面直接傳回顯示,無需等待下載下傳 js 檔案及再去渲染等,是以 SSR 有更快的内容到達時間;
(2) 服務端渲染的缺點:
- 更多的開發條件限制:例如服務端渲染隻支援 beforCreate 和 created 兩個鈎子函數,這會導緻一些外部擴充庫需要特殊處理,才能在服務端渲染應用程式中運作;并且與可以部署在任何靜态檔案伺服器上的完全靜态單頁面應用程式 SPA 不同,服務端渲染應用程式,需要處于 Node.js server 運作環境;
- 更多的伺服器負載:在 Node.js 中渲染完整的應用程式,顯然會比僅僅提供靜态檔案的 server 更加大量占用CPU 資源 (CPU-intensive - CPU 密集),是以如果你預料在高流量環境 ( high traffic ) 下使用,請準備相應的伺服器負載,并明智地采用緩存政策。
19. vue-router 路由模式有幾種?
vue-router 有 3 種路由模式:hash、history、abstract,對應的源碼如下所示:
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}`)
}
}
複制代碼
其中,3 種路由模式的說明如下:
- hash: 使用 URL hash 值來作路由。支援所有浏覽器,包括不支援 HTML5 History Api 的浏覽器;
- history : 依賴 HTML5 History API 和伺服器配置。具體可以檢視 HTML5 History 模式;
- abstract : 支援所有 JavaScript 運作環境,如 Node.js 伺服器端。如果發現沒有浏覽器的 API,路由會自動強制進入這個模式.
20. 能說下 vue-router 中常用的 hash 和 history 路由模式實作原理嗎?
(1)hash 模式的實作原理
早期的前端路由的實作就是基于 location.hash 來實作的。其實作原理很簡單,location.hash 的值就是 URL 中 # 後面的内容。比如下面這個網站,它的 location.hash 的值為 '#search':
https://www.word.com#search
複制代碼
hash 路由模式的實作主要是基于下面幾個特性:
- URL 中 hash 值隻是用戶端的一種狀态,也就是說當向伺服器端送出請求時,hash 部分不會被發送;hash 值的改變,都會在浏覽器的通路曆史中增加一個記錄。是以我們能通過浏覽器的回退、前進按鈕控制hash 的切換;
- 可以通過 a 标簽,并設定 href 屬性,當使用者點選這個标簽後,URL 的 hash 值會發生改變;或者使用 JavaScript 來對 loaction.hash 進行指派,改變 URL 的 hash 值;
- 我們可以使用 hashchange 事件來監聽 hash 值的變化,進而對頁面進行跳轉(渲染)。
(2)history 模式的實作原理
HTML5 提供了 History API 來實作 URL 的變化。其中做最主要的 API 有以下兩個:history.pushState() 和 history.repalceState()。這兩個 API 可以在不進行重新整理的情況下,操作浏覽器的曆史紀錄。唯一不同的是,前者是新增一個曆史記錄,後者是直接替換目前的曆史記錄,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
複制代碼
history 路由模式的實作主要基于存在下面幾個特性:
- pushState 和 repalceState 兩個 API 來操作實作 URL 的變化 ;
- 我們可以使用 popstate 事件來監聽 url 的變化,進而對頁面進行跳轉(渲染);
- history.pushState() 或 history.replaceState() 不會觸發 popstate 事件,這時我們需要手動觸發頁面跳轉(渲染)。
21. Vue 架構怎麼實作對象和數組的監聽?
如果被問到 Vue 怎麼實作資料雙向綁定,大家肯定都會回答 通過 Object.defineProperty() 對資料進行劫持,但是 Object.defineProperty() 隻能對屬性進行資料劫持,不能對整個對象進行劫持,同理無法對數組進行劫持,但是我們在使用 Vue 架構中都知道,Vue 能檢測到對象和數組(部分方法的操作)的變化,那它是怎麼實作的呢?我們檢視相關代碼如下:
/**
* Observe a list of Array items.
*/
observeArray (items: Array) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]) // observe 功能為監測資料的變化
}
}
/**
* 對屬性進行遞歸周遊
*/
let childOb = !shallow && observe(val) // observe 功能為監測資料的變化
複制代碼
通過以上 Vue 源碼部分檢視,我們就能知道 Vue 架構是通過周遊數組 和遞歸周遊對象,進而達到利用 Object.defineProperty() 也能對對象和數組(部分方法的操作)進行監聽。
22. Proxy 與 Object.defineProperty 優劣對比
Proxy 的優勢如下:
- Proxy 可以直接監聽對象而非屬性;
- Proxy 可以直接監聽數組的變化;
- Proxy 有多達 13 種攔截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具備的;
- Proxy 傳回的是一個新對象,我們可以隻操作新的對象達到目的,而 Object.defineProperty 隻能周遊對象屬性直接修改;Proxy 作為新标準将受到浏覽器廠商重點持續的性能優化,也就是傳說中的新标準的性能紅利;
Object.defineProperty 的優勢如下:
- 相容性好,支援 IE9,而 Proxy 的存在浏覽器相容性問題,而且無法用 polyfill 磨平,是以 Vue 的作者才聲明需要等到下個大版本( 3.0 )才能用 Proxy 重寫。
23. Vue 怎麼用 vm.$set() 解決對象新增屬性不能響應的問題 ?
受現代 JavaScript 的限制 ,Vue 無法檢測到對象屬性的添加或删除。由于 Vue 會在初始化執行個體時對屬性執行 getter/setter 轉化,是以屬性必須在 data 對象上存在才能讓 Vue 将它轉換為響應式的。但是 Vue 提供了
Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
來實作為對象添加響應式屬性,那架構本身是如何實作的呢?
我們檢視對應的 Vue 源碼:
vue/src/core/instance/index.js
export function set (target: Array | Object, key: any, val: any): any {
// target 為數組
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改數組的長度, 避免索引>數組長度導緻splcie()執行有誤
target.length = Math.max(target.length, key)
// 利用數組的splice變異方法觸發響應式
target.splice(key, 1, val)
return val
}
// key 已經存在,直接修改屬性值
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// target 本身就不是響應式資料, 直接指派
if (!ob) {
target[key] = val
return val
}
// 對屬性進行響應式處理
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
複制代碼
我們閱讀以上源碼可知,vm.$set 的實作原理是:
- 如果目标是數組,直接使用數組的 splice 方法觸發相應式;
- 如果目标是對象,會先判讀屬性是否存在、對象是否是響應式,最終如果要對屬性進行響應式處理,則是通過調用 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化對象時,給對象屬性采用 Object.defineProperty 動态添加 getter 和 setter 的功能所調用的方法)
24. 虛拟 DOM 的優缺點?
優點:
- 保證性能下限:架構的虛拟 DOM 需要适配任何上層 API 可能産生的操作,它的一些 DOM 操作的實作必須是普适的,是以它的性能并不是最優的;但是比起粗暴的 DOM 操作性能要好很多,是以架構的虛拟 DOM 至少可以保證在你不需要手動優化的情況下,依然可以提供還不錯的性能,即保證性能的下限;
- 無需手動操作 DOM:我們不再需要手動去操作 DOM,隻需要寫好 View-Model 的代碼邏輯,架構會根據虛拟 DOM 和 資料雙向綁定,幫我們以可預期的方式更新視圖,極大提高我們的開發效率;
- 跨平台:虛拟 DOM 本質上是 JavaScript 對象,而 DOM 與平台強相關,相比之下虛拟 DOM 可以進行更友善地跨平台操作,例如伺服器渲染、weex 開發等等。
缺點:
- 無法進行極緻優化:雖然虛拟 DOM + 合理的優化,足以應對絕大部分應用的性能需求,但在一些性能要求極高的應用中虛拟 DOM 無法進行針對性的極緻優化。
25. 虛拟 DOM 實作原理?
虛拟 DOM 的實作原理主要包括以下 3 部分:
- 用 JavaScript 對象模拟真實 DOM 樹,對真實 DOM 進行抽象;
- diff 算法 — 比較兩棵虛拟 DOM 樹的差異;
- pach 算法 — 将兩個虛拟 DOM 對象的差異應用到真正的 DOM 樹。
- 虛拟DOM詳細文章
26. v-html相關
- v-html 如何解決xxs攻擊及原理
27. Vue 中的 key 有什麼作用?
key 是為 Vue 中 vnode 的唯一标記,通過這個 key,我們的 diff 操作可以更準确、更快速。Vue 的 diff 過程可以概括為:oldCh 和 newCh 各有兩個頭尾的變量,它們會新節點和舊節點會進行兩兩對比,即一共有4種比較方式:
oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex
,如果以上 4 種比較都沒比對,如果設定了key,就會用 key 再進行比較,在比較的過程中,周遊會往中間靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一個已經周遊完了,就會結束比較。
newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex
是以 Vue 中 key 的作用是:key 是為 Vue 中 vnode 的唯一标記,通過這個 key,我們的 diff 操作可以更準确、更快速
更準确:因為帶 key 就不是就地複用了,在 sameNode 函數 a.key === b.key 對比中可以避免就地複用的情況。是以會更加準确。
更快速:利用 key 的唯一性生成 map 對象來擷取對應節點,比周遊方式更快,源碼如下:
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
}
複制代碼
28.你有對 Vue 項目進行哪些優化?
如果沒有對 Vue 項目沒有進行過優化總結的同學,可以參考另一篇文章《 Vue 項目性能優化 — 實踐指南 》,文章主要介紹從 3 個大方面,22 個小方面詳細講解如何進行 Vue 項目的優化。
(1)代碼層面的優化
- v-if 和 v-show 區分使用場景
- computed 和 watch 區分使用場景
- v-for 周遊必須為 item 添加 key,且避免同時使用 v-if
- 長清單性能優化
- 事件的銷毀
- 圖檔資源懶加載
- 路由懶加載
- 第三方插件的按需引入
- 優化無限清單性能
- 服務端渲染 SSR or 預渲染
(2)Webpack 層面的優化
- Webpack 對圖檔進行壓縮
- 減少 ES6 轉為 ES5 的備援代碼
- 提取公共代碼
- 模闆預編譯
- 提取元件的 CSS
- 優化 SourceMap
- 建構結果輸出分析
- Vue 項目的編譯優化
(3)基礎的 Web 技術的優化
- 開啟 gzip 壓縮
- 浏覽器緩存
- CDN 的使用
- 使用 Chrome Performance 查找性能瓶頸
29. 子產品化
- 基本概念:
- 在js中,一個子產品就是實作特定功能的檔案(js檔案)
- 遵循子產品的機制,想要什麼就加載什麼子產品
- 子產品化開發需要遵循規範
- js實作子產品化規範
1.AMD 浏覽器 requirejs 子產品被異步加載, 子產品加載不影響後面語句的運作 預設使用baseURL+ paths的路經解析方式 2.CommonJS nodejs 3.ES6的import/export 4.CMD 浏覽器端 複制代碼
- 解決的問題:1.命名沖突 2.檔案依賴 3.子產品的複用 4.統一規範和開發方式
30. 談談Vue和React元件化的思想
- 1.我們在各個頁面開發的時候,會産生很多重複的功能,比如element中的xxxx。像這種純粹非頁面的UI,便成為我們常用的UI元件,最初的前端元件也就僅僅指的是UI元件
- 2.随着業務邏輯變得越來多是,我們就想要我們的元件可以處理很多事,這就是我們常說的元件化,這個元件就不是UI元件了,而是包具體業務的業務元件
- 3.這種開發思想就是分而治之。最大程度的降低開發難度和維護成本的效果。并且可以多人協作,每個人寫不同的元件,最後像撘積木一樣的把它構成一個頁面
31. 對于即将到來的 vue3.0 特性你有什麼了解的嗎?
Vue 3.0 正走在釋出的路上,Vue 3.0 的目标是讓 Vue 核心變得更小、更快、更強大,是以 Vue 3.0 增加以下這些新特性:
(1)監測機制的改變
3.0 将帶來基于代理 Proxy 的 observer 實作,提供全語言覆寫的反應性跟蹤。這消除了 Vue 2 當中基于 Object.defineProperty 的實作所存在的很多限制:
- 隻能監測屬性,不能監測對象
- 檢測屬性的添加和删除;
- 檢測數組索引和長度的變更;
- 支援 Map、Set、WeakMap 和 WeakSet。
新的 observer 還提供了以下特性:
- 用于建立 observable 的公開 API。這為中小規模場景提供了簡單輕量級的跨元件狀态管了解決方案。
- 預設采用惰性觀察。在 2.x 中,不管反應式資料有多大,都會在啟動時被觀察到。如果你的資料集很大,這可能會在應用啟動時帶來明顯的開銷。在 3.x 中,隻觀察用于渲染應用程式最初可見部分的資料。
- 更精确的變更通知。在 2.x 中,通過 Vue.set 強制添加新屬性将導緻依賴于該對象的 watcher 收到變更通知。在 3.x 中,隻有依賴于特定屬性的 watcher 才會收到通知。
- 不可變的 observable:我們可以建立值的“不可變”版本(即使是嵌套屬性),除非系統在内部暫時将其“解禁”。這個機制可用于當機 prop 傳遞或 Vuex 狀态樹以外的變化。
- 更好的調試功能:我們可以使用新的 renderTracked 和 renderTriggered 鈎子精确地跟蹤元件在什麼時候以及為什麼重新渲染。
(2)模闆
模闆方面沒有大的變更,隻改了作用域插槽,2.x 的機制導緻作用域插槽變了,父元件會重新渲染,而 3.0 把作用域插槽改成了函數的方式,這樣隻會影響子元件的重新渲染,提升了渲染的性能。
同時,對于 render 函數的方面,vue3.0 也會進行一系列更改來友善習慣直接使用 api 來生成 vdom 。
(3)對象式的元件聲明方式
vue2.x 中的元件是通過聲明的方式傳入一系列 option,和 TypeScript 的結合需要通過一些裝飾器的方式來做,雖然能實作功能,但是比較麻煩。3.0 修改了元件的聲明方式,改成了類式的寫法,這樣使得和 TypeScript 的結合變得很容易。
此外,vue 的源碼也改用了 TypeScript 來寫。其實當代碼的功能複雜之後,必須有一個靜态類型系統來做一些輔助管理。現在 vue3.0 也全面改用 TypeScript 來重寫了,更是使得對外暴露的 api 更容易結合 TypeScript。靜态類型系統對于複雜代碼的維護确實很有必要。
(4)其它方面的更改
vue3.0 的改變是全面的,上面隻涉及到主要的 3 個方面,還有一些其他的更改:
- 支援自定義渲染器,進而使得 weex 可以通過自定義渲染器的方式來擴充,而不是直接 fork 源碼來改的方式。
- 支援 Fragment(多個根節點)和 Protal(在 dom 其他部分渲染組建内容)元件,針對一些特殊的場景做了處理。
- 基于 treeshaking 優化,提供了更多的内置功能。
32. 你知道vue的模闆文法用的是哪個web模闆引擎的嗎?說說你對這模闆引擎的了解
Vue使用了Mustache文法,即雙大括号的文法。
模闆引擎:負責組裝資料,以另外一種形式或外觀展現資料。
優點:
- 可維護性(後期改起來友善);
- 可擴充性(想要增加功能,增加需求友善);
- 開發效率提高(程式邏輯組織更好,調試友善);
- 看起來舒服(不容易寫錯)
32. 你知道vue的模闆文法用的是哪個web模闆引擎的嗎?說說你對這模闆引擎的了解
Vue使用了Mustache文法,即雙大括号的文法。
模闆引擎:負責組裝資料,以另外一種形式或外觀展現資料。
優點:
- 可維護性(後期改起來友善);
- 可擴充性(想要增加功能,增加需求友善);
- 開發效率提高(程式邏輯組織更好,調試友善);
- 看起來舒服(不容易寫錯)
33. 怎麼給vue定義全局的方法?
第一種:挂載到Vue的prototype上。把全局方法寫到一個檔案裡面,然後for循環挂載到Vue的prototype上,缺點是調用這個方法的時候沒有提示
Object.keys(tools).forEach(key => {
Vue.prototype[key] = tools[key]
})
複制代碼
第二種:利用全局混入mixin,因為mixin裡面的methods會和建立的每個單檔案元件合并。這樣做的優點是調用這個方法的時候有提示
Vue.mixin(mixin)
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')
import tools from "./tools"
import filters from "./filters"
import Config from '../config'
import CONSTANT from './const_var'
export default {
data() {
return {
CONFIG: Config,
CONSTANT: CONSTANT
}
},
methods: {
// //将tools裡面的方法挂載到vue上,以友善調用,直接this.$xxx方法名就可以了
// Object.keys(tools).forEach(key => {
// Vue.prototype[key] = tools[key]
// })
//将tools裡面的方法用對象展開符混入到mixin上,以友善調用,直接this.$xxx方法名就可以了
...tools
},
filters: {
// //将filter裡面的方法添加了vue的篩選器上
// Object.keys(filters).forEach(key => {
// Vue.filter(key, filters[key])
// })
...filters
}
}
複制代碼
34. vue2.0不再支援v-html中使用過濾器了怎麼辦?
- 在method中定義方法
htmlFilter(htmlString){
return htmlString.replace(/+s/g,'')
}
複制代碼
- 在vue中 v-html="htmlFilter(htmlString)"即可
35. 怎麼解決vue打包後靜态資源圖檔失效的問題?
設定assetsPublicPath将 assetsPublicPath: '/' 改為 assetsPublicPath: './'
最新的vue-cli 需要在根目錄下建一個vue.config.js 在裡面配置publicPath即可
作者:小小洋
連結:https://juejin.im/post/6876002080235274247