2021vue面試題+答案
vue視頻教程系列:
Vue3+ElementPlus+Koa2 全棧開發后台系統
視頻課程:點擊觀看
完整課程目錄:點擊查看
Vue3.0高階實戰:開發高質量音樂Web app
視頻課程:點擊觀看
完整課程目錄:點擊查看
VUE全面教學+VUE開源項目超級實戰:
視頻課程:點擊觀看
完整課程目錄:點擊查看
最新Vue.JS教程快速入門到項目實戰(Vue3/VueJS技術詳解)
視頻課程:點擊觀看
完整課程目錄:點擊查看
最新最全前端畢設項目(小程序+VUE+Noed+React+uni app+Express+Mongodb)
視頻課程:點擊觀看
完整課程目錄:點擊查看
v-show 與 v-if 有什么區別?
v-if 是真正的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建;也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變為真時,才會開始渲染條件塊。
v-show 就簡單得多——不管初始條件是什么,元素總是會被渲染,並且只是簡單地基於 CSS 的 “display” 屬性進行切換。
所以,v-if 適用於在運行時很少改變條件,不需要頻繁切換條件的場景;v-show 則適用於需要非常頻繁切換條件的場景。
computed 和 watch 的區別和運用的場景?
computed: 是計算屬性,依賴其它屬性值,並且 computed 的值有緩存,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值;
watch: 更多的是「觀察」的作用,類似於某些數據的監聽回調 ,每當監聽的數據變化時都會執行回調進行后續操作;
運用場景:
- 當我們需要進行數值計算,並且依賴於其它數據時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次獲取值時,都要重新計算;
- 當我們需要在數據變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行異步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
Vue 組件通訊有哪幾種方式
- props 和$emit 父組件向子組件傳遞數據是通過 prop 傳遞的,子組件傳遞數據給父組件是通過$emit 觸發事件來做到的
- $parent,$children 獲取當前組件的父組件和當前組件的子組件
- $attrs 和$listeners A->B->C。Vue 2.4 開始提供了$attrs 和$listeners 來解決這個問題
- 父組件中通過 provide 來提供變量,然后在子組件中通過 inject 來注入變量。(官方不推薦在實際業務中使用,但是寫組件庫時很常用)
- $refs 獲取組件實例
- envetBus 兄弟組件數據傳遞 這種情況下可以使用事件總線的方式
- vuex 狀態管理
Vue 的生命周期方法有哪些 一般在哪一步發請求
beforeCreate 在實例初始化之后,數據觀測(data observer) 和 event/watcher 事件配置之前被調用。在當前階段 data、methods、computed 以及 watch 上的數據和方法都不能被訪問
created 實例已經創建完成之后被調用。在這一步,實例已完成以下的配置:數據觀測(data observer),屬性和方法的運算, watch/event 事件回調。這里沒有$el,如果非要想與 Dom 進行交互,可以通過 vm.$nextTick 來訪問 Dom
beforeMount 在掛載開始之前被調用:相關的 render 函數首次被調用。
mounted 在掛載完成后發生,在當前階段,真實的 Dom 掛載完畢,數據完成雙向綁定,可以訪問到 Dom 節點
beforeUpdate 數據更新時調用,發生在虛擬 DOM 重新渲染和打補丁(patch)之前。可以在這個鈎子中進一步地更改狀態,這不會觸發附加的重渲染過程
updated 發生在更新完成之后,當前階段組件 Dom 已完成更新。要注意的是避免在此期間更改數據,因為這可能會導致無限循環的更新,該鈎子在服務器端渲染期間不被調用。
beforeDestroy 實例銷毀之前調用。在這一步,實例仍然完全可用。我們可以在這時進行善后收尾工作,比如清除計時器。
destroyed Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。 該鈎子在服務器端渲染期間不被調用。
activated keep-alive 專屬,組件被激活時調用
deactivated keep-alive 專屬,組件被銷毀時調用
異步請求在哪一步發起?
可以在鈎子函數 created、beforeMount、mounted 中進行異步請求,因為在這三個鈎子函數中,data 已經創建,可以將服務端端返回的數據進行賦值。
如果異步請求不需要依賴 Dom 推薦在 created 鈎子函數中調用異步請求,因為在 created 鈎子函數中調用異步請求有以下優點:
- 能更快獲取到服務端數據,減少頁面 loading 時間;
- ssr 不支持 beforeMount 、mounted 鈎子函數,所以放在 created 中有助於一致性;
Vue3.0 和 2.0 的響應式原理區別
Vue3.x 改用 Proxy 替代 Object.defineProperty。因為 Proxy 可以直接監聽對象和數組的變化,並且有多達 13 種攔截方法。
相關代碼如下
import { mutableHandlers } from "./baseHandlers"; // 代理相關邏輯
import { isObject } from "./util"; // 工具方法
export function reactive(target) {
// 根據不同參數創建不同響應式對象
return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
if (!isObject(target)) {
return target;
}
const observed = new Proxy(target, baseHandler);
return observed;
}
const get = createGetter();
const set = createSetter();
function createGetter() {
return function get(target, key, receiver) {
// 對獲取的值進行放射
const res = Reflect.get(target, key, receiver);
console.log("屬性獲取", key);
if (isObject(res)) {
// 如果獲取的值是對象類型,則返回當前對象的代理對象
return reactive(res);
}
return res;
};
}
function createSetter() {
return function set(target, key, value, receiver) {
const oldValue = target[key];
const hadKey = hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
console.log("屬性新增", key, value);
} else if (hasChanged(value, oldValue)) {
console.log("屬性值被修改", key, value);
}
return result;
};
}
export const mutableHandlers = {
get, // 當獲取屬性時調用此方法
set, // 當修改屬性時調用此方法
};
Vue3.0 和 2.0 的響應式原理區別
Vue3.x 改用 Proxy 替代 Object.defineProperty。因為 Proxy 可以直接監聽對象和數組的變化,並且有多達 13 種攔截方法。
相關代碼如下
import { mutableHandlers } from "./baseHandlers"; // 代理相關邏輯
import { isObject } from "./util"; // 工具方法
export function reactive(target) {
// 根據不同參數創建不同響應式對象
return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
if (!isObject(target)) {
return target;
}
const observed = new Proxy(target, baseHandler);
return observed;
}
const get = createGetter();
const set = createSetter();
function createGetter() {
return function get(target, key, receiver) {
// 對獲取的值進行放射
const res = Reflect.get(target, key, receiver);
console.log("屬性獲取", key);
if (isObject(res)) {
// 如果獲取的值是對象類型,則返回當前對象的代理對象
return reactive(res);
}
return res;
};
}
function createSetter() {
return function set(target, key, value, receiver) {
const oldValue = target[key];
const hadKey = hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
console.log("屬性新增", key, value);
} else if (hasChanged(value, oldValue)) {
console.log("屬性值被修改", key, value);
}
return result;
};
}
export const mutableHandlers = {
get, // 當獲取屬性時調用此方法
set, // 當修改屬性時調用此方法
};
為什么在 Vue3.0 采用了 Proxy,拋棄了 Object.defineProperty?
Object.defineProperty 本身有一定的監控到數組下標變化的能力,但是在 Vue 中,從性能/體驗的性價比考慮,尤大大就棄用了這個特性(Vue 為什么不能檢測數組變動 )。為了解決這個問題,經過 vue 內部處理后可以使用以下幾種方法來監聽數組
push();
pop();
shift();
unshift();
splice();
sort();
reverse();
復制代碼
由於只針對了以上 7 種方法進行了 hack 處理,所以其他數組的屬性也是檢測不到的,還是具有一定的局限性。
Object.defineProperty 只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷。Vue 2.x 里,是通過 遞歸 + 遍歷 data 對象來實現對數據的監控的,如果屬性值也是對象那么需要深度遍歷,顯然如果能劫持一個完整的對象是才是更好的選擇。
Proxy 可以劫持整個對象,並返回一個新的對象。Proxy 不僅可以代理對象,還可以代理數組。還可以代理動態增加的屬性。
computed 的實現原理
computed 本質是一個惰性求值的觀察者。
computed 內部實現了一個惰性的 watcher,也就是 computed watcher,computed watcher 不會立刻求值,同時持有一個 dep 實例。
其內部通過 this.dirty 屬性標記計算屬性是否需要重新求值。
當 computed 的依賴狀態發生改變時,就會通知這個惰性的 watcher,
computed watcher 通過 this.dep.subs.length 判斷有沒有訂閱者,
有的話,會重新計算,然后對比新舊值,如果變化了,會重新渲染。 (Vue 想確保不僅僅是計算屬性依賴的值發生變化,而是當計算屬性最終計算的值發生變化時才會觸發渲染 watcher 重新渲染,本質上是一種優化。)
沒有的話,僅僅把 this.dirty = true。 (當計算屬性依賴於其他數據時,屬性並不會立即重新計算,只有之后其他地方需要讀取屬性的時候,它才會真正計算,即具備 lazy(懶計算)特性。)
Vue 中的 key 到底有什么用?
key 是給每一個 vnode 的唯一 id,依靠 key,我們的 diff 操作可以更准確、更快速 (對於簡單列表頁渲染來說 diff 節點也更快,但會產生一些隱藏的副作用,比如可能不會產生過渡效果,或者在某些節點有綁定數據(表單)狀態,會出現狀態錯位。)
diff 算法的過程中,先會進行新舊節點的首尾交叉對比,當無法匹配的時候會用新節點的 key 與舊節點進行比對,從而找到相應舊節點.
更准確 : 因為帶 key 就不是就地復用了,在 sameNode 函數 a.key === b.key 對比中可以避免就地復用的情況。所以會更加准確,如果不加 key,會導致之前節點的狀態被保留下來,會產生一系列的 bug。
更快速 : key 的唯一性可以被 Map 數據結構充分利用,相比於遍歷查找的時間復雜度 O(n),Map 的時間復雜度僅僅為 O(1),源碼如下:
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;
}
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 表單元素為例:
<input v-model='something'>
相當於
<input v-bind:value="something" v-on:input="something = $event.target.value">
復制代碼
如果在自定義組件中,v-model 默認會利用名為 value 的 prop 和名為 input 的事件,如下所示:
父組件:
<ModelChild v-model="message"></ModelChild>
子組件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小紅')
},
},
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 表單元素為例:
<input v-model='something'>
相當於
<input v-bind:value="something" v-on:input="something = $event.target.value">
復制代碼
如果在自定義組件中,v-model 默認會利用名為 value 的 prop 和名為 input 的事件,如下所示:
父組件:
<ModelChild v-model="message"></ModelChild>
子組件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小紅')
},
},
什么是 MVVM?
Model–View–ViewModel (MVVM) 是一個軟件架構設計模式,由微軟 WPF 和 Silverlight 的架構師 Ken Cooper 和 Ted Peters 開發,是一種簡化用戶界面的事件驅動編程方式。由 John Gossman(同樣也是 WPF 和 Silverlight 的架構師)於2005年在他的博客上發表
MVVM 源自於經典的 Model–View–Controller(MVC)模式 ,MVVM 的出現促進了前端開發與后端業務邏輯的分離,極大地提高了前端開發效率,MVVM 的核心是 ViewModel 層,它就像是一個中轉站(value converter),負責轉換 Model 中的數據對象來讓數據變得更容易管理和使用,該層向上與視圖層進行雙向數據綁定,向下與 Model 層通過接口請求進行數據交互,起呈上啟下作用
(1)View 層
View 是視圖層,也就是用戶界面。前端主要由 HTML 和 CSS 來構建 。
(2)Model 層
Model 是指數據模型,泛指后端進行的各種業務邏輯處理和數據操控,對於前端來說就是后端提供的 api 接口。
(3)ViewModel 層
ViewModel 是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從后端獲取的 Model 數據進行轉換處理,做二次封裝,以生成符合 View 層使用預期的視圖數據模型。需要注意的是 ViewModel 所封裝出來的數據模型包括視圖的狀態和行為兩部分,而 Model 層的數據模型是只包含狀態的,比如頁面的這一塊展示什么,而頁面加載進來時發生什么,點擊這一塊發生什么,這一塊滾動時發生什么這些都屬於視圖行為(交互),視圖狀態和行為都封裝在了 ViewModel 里。這樣的封裝使得 ViewModel 可以完整地去描述 View 層。
MVVM 框架實現了雙向綁定,這樣 ViewModel 的內容會實時展現在 View 層,前端開發者再也不必低效又麻煩地通過操縱 DOM 去更新視圖,MVVM 框架已經把最臟最累的一塊做好了,我們開發者只需要處理和維護 ViewModel,更新數據視圖就會自動得到相應更新。這樣 View 層展現的不是 Model 層的數據,而是 ViewModel 的數據,由 ViewModel 負責與 Model 層交互,這就完全解耦了 View 層和 Model 層,這個解耦是至關重要的,它是前后端分離方案實施的重要一環。
我們以下通過一個 Vue 實例來說明 MVVM 的具體實現,有 Vue 開發經驗的同學應該一目了然:
(1)View 層
<div id="app">
<p>{{message}}</p>
<button v-on:click="showMessage()">Click me</button>
</div>
(2)ViewModel 層
var app = new Vue({
el: '#app',
data: { // 用於描述視圖狀態
message: 'Hello Vue!',
},
methods: { // 用於描述視圖行為
showMessage(){
let vm = this;
alert(vm.message);
}
},
created(){
let vm = this;
// Ajax 獲取 Model 層的數據
ajax({
url: '/your/server/data/api',
success(res){
vm.message = res;
}
});
}
})
(3) Model 層
{
"url": "/your/server/data/api",
"res": {
"success": true,
"name": "IoveC",
"domain": "www.cnblogs.com"
}
}