首頁> web前端> Vue.js> 主體

2023年vue高頻面試題分享(附答案分析)

青灯夜游
發布: 2022-12-15 20:04:35
轉載
4027 人瀏覽過

這篇文章為大家總結一些值得收藏的2023年精選vue高頻面試題(附答案)。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

2023年vue高頻面試題分享(附答案分析)

Vue-router 導覽守衛有哪些

  • 全域前置/鉤子:beforeEach、beforeResolve、afterEach
  • 路由獨享的守衛:beforeEnter
  • 元件內的守衛:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

#(學習影片分享:vue影片教學

#(學習影片分享:
vue影片教學

# )

為什麼在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 不僅可以代理物件,還可以代理數組。也可以代理動態增加的屬性。
  • v-for 為什麼要加key

  • 如果不使用key,Vue 會使用一種最大限度減少動態元素並且盡可能的嘗試就地修改/重複使用相同類型元素的演算法。 key 是為Vue 中vnode 的唯一標記,透過這個key,我們的diff 運算可以更準確、更快速
  • ##更準確

    :因為帶key 就不是就地復用了,在sameNode 函數a.key === b.key 對比中可以避免就地復用的情況。所以會更加準確。

更快

:利用key 的唯一性產生map 物件來取得對應節點,比遍歷方式更快

  • 如何從真實DOM到虛擬DOM##涉及到Vue中的模板編譯原理,主要過程:

  • 將模板轉換成
  • ast

    樹,

    ast
  • 用物件來描述真實的JS語法(將真實DOM轉換成虛擬DOM)
  • 最佳化樹

#將ast樹產生程式碼

#為什麼Vue採用非同步渲染?

Vue

是元件級更新,如果不採用非同步更新,那麼每次更新資料都會對目前元件進行重新渲染,所以為了效能,Vue會在本輪資料更新後,在非同步更新視圖。核心思想nextTickdep.notify()通知watcher進行更新,subs[i].update依序呼叫watcher 的

update

queueWatcher將watcher 去重放入佇列, nextTick(

flushSchedulerQueue

)在下一tick中刷新watcher隊列(非同步)。

為什麼vue元件中data必須是一個函數?

對象為參考類型,當複用元件時,由於資料對像都指向同一個data對象,當在一個元件中修改data時,其他重用的元件中的data會同時被修改;而使用傳回物件的函數,由於每次傳回的都是一個新物件(Object的實例),引用位址不同,則不會出現這個問題。

MVC 與MVVM 區別

  • MVC
  • MVC 全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟體設計典範
Model(模型):是應用程式中用來處理應用程式資料邏輯的部分。通常模型物件負責在資料庫中存取資料

View(檢視):是應用程式中處理資料顯示的部分。通常視圖是依據模型資料建立的

Controller(控制器):是應用程式中處理使用者互動的部分。通常控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據

MVC 的想法:一句話描述就是Controller 負責將Model 的數據用View 顯示出來,換句話說就是在Controller 裡面把Model 的資料賦值給View。

######MVVM#########MVVM 新增了 VM 類別###
  • ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想)

整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作 DOM 元素。因为在 MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也观察不到 View,这种低耦合模式提高代码的可重用性

注意:Vue 并没有完全遵循 MVVM 的思想 这一点官网自己也有说明

那么问题来了 为什么官方要说 Vue 没有完全遵循 MVVM 思想呢?

  • 严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM。

Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?你能说说如下代码的实现原理么?

1)Vue为什么要用vm.$set() 解决对象新增属性不能响应的问题

  • Vue使用了Object.defineProperty实现双向数据绑定

  • 在初始化实例时对属性执行 getter/setter 转化

  • 属性必须在data对象上存在才能让Vue将它转换为响应式的(这也就造成了Vue无法检测到对象属性的添加或删除)

所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)

2)接下来我们看看框架本身是如何实现的呢?

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 的功能所调用的方法

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, // 当修改属性时调用此方法 };
登入後複製

Vue模版编译原理知道吗,能简单说一下吗?

简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:

  • 生成AST树
  • 优化
  • codegen

首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。

Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最后一步是将优化后的AST树转换为可执行的代码

MVVM的优缺点?

优点:

  • 分离视图(View)和模型(Model),降低代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)可以独⽴于Model变化和修改,⼀个ViewModel可以绑定不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。你可以把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑
  • 提⾼可测试性: ViewModel的存在可以帮助开发者更好地编写测试代码
  • ⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放

缺点:

  • Bug很難被調試: 因為使⽤雙向綁定的模式,當你看到界⾯異常了,有可能是你View的程式碼有Bug,也可能是Model的程式碼有問題。資料綁定使得⼀個位置的Bug被快速傳遞到別的位置,要定位原始出問題的地⽅就變得不那麼容易了。另外,資料綁定的宣告是指令式地寫在View的模版當中的,這些內容是沒辦法去打斷點debug的
  • ⼀個⼤的模組中model也會很⼤,雖然使⽤⽅便了也很容易保證了資料的⼀致性,當時⻓期持有,不釋放內存就造成了花費更多的內存
  • 對於⼤型的圖形應⽤程序,視圖狀態較多,ViewModel的建置與維護的成本都會⽐較⾼。

Vue data 中某一個屬性的值改變後,視圖會立即同步執行重新渲染嗎?

不會立即同步執行重新渲染。 Vue 實作響應式並不是資料變更後 DOM 立即變化,而是依照一定的策略進行 DOM 的更新。 Vue 在更新 DOM 時是非同步執行的。只要偵聽到資料變化, Vue 將開啟一個佇列,並緩衝在同一事件循環中發生的所有資料變更。

如果同一個watcher被多次觸發,只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 DOM 操作是非常重要的。然後,在下一個的事件循環tick中,Vue 刷新佇列並執行實際(已去重的)工作。

diff演算法

時間複雜度:個樹的完全diff演算法是一個時間複雜度為O(n*3),vue進行最佳化轉換成O(n)

理解:

  • 最小量更新,key很重要。這個可以是這個節點的唯一標識,告訴diff演算法,在更改前後它們是同一個DOM節點

    • 擴展v-for為什麼要有key,沒有key會暴力復用,舉例子的話隨便說一個比如移動節點或增加節點(修改DOM),加key只會移動減少操作DOM。
  • 只有是同一個虛擬節點才會進行精細化比較,否則就是暴力刪除舊的,插入新的。
  • 只進行同層比較,不會進行跨層比較。

diff演算法的最佳化策略:四個命中查找,四個指標

  1. 舊前與新前(先比開頭,後插入和刪除節點的這種情況)
  2. 舊後與新後(比結尾,前插入或刪除的情況)
  3. 舊前與新後(頭與尾比,此種發生了,涉及移動節點,那麼新前指向的節點,移動到舊後之後)
  4. 舊後與新前(尾與頭比,此種發生了,涉及移動節點,那麼新前指向的節點,移動到舊前之前)

--- 問完上面這些如果都能很清楚的話,基本O了---

以下的這些簡單的概念,你肯定也是沒有問題的啦?

Vue的優點

  • #輕量級框架:只專注於視圖層,是一個建構資料的視圖集合,大小只有幾十kb
  • 簡單易學:國人開發,中文文檔,不存在語言障礙,易於理解和學習;
  • 雙向資料綁定:保留了angular的特點,在資料操作方面更為簡單;
  • 元件化:保留了react的優點,實作了html的封裝和重用,在構建單頁應用方面有著獨特的優勢;
  • 視圖,數據,結構分離:使數據的更改更為簡單,不需要進行邏輯代碼的修改,只需要操作數據就能完成相關操作;
  • 虛擬DOM:dom操作是非常耗費效能的,不再使用原生的dom操作節點,極大解放dom操作,但具體操作的還是dom不過是換了另一種方式;
  • 運行速度更快:相比較於react而言,同樣是操作虛擬dom,就效能而言,vue存在很大的優勢。

vue-router 路由鉤子函數是什麼執行順序是什麼

路由鉤子的執行流程, 鉤子函數種類有:全域守衛、路由守衛、元件守衛

完整的導航解析流程:

  • #導航被觸發。

  • 在失活的元件裡呼叫 beforeRouteLeave 守衛。

  • 呼叫全域的 beforeEach 守衛。

  • 在重複使用的元件裡呼叫 beforeRouteUpdate 守衛 (2.2 )。

  • 在路由配置裡呼叫 beforeEnter。

  • 解析非同步路由元件。

  • 在被啟動的元件裡呼叫 beforeRouteEnter。

  • 呼叫全域的 beforeResolve 守衛 (2.5 )。

  • 導航被確認。

  • 呼叫全域的 afterEach 鉤子。

  • 觸發 DOM 更新。

  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

Vue.js的template编译

简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点),详细步骤如下:

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

$nextTick 是什么?

Vue 实现响应式并不是在数据发生后立即更新 DOM,使用vm.$nextTick是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM

说说Vue的生命周期吧

什么时候被调用?

  • beforeCreate :实例初始化之后,数据观测之前调用
  • created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、watch/event事件回调。无$el.
  • beforeMount:在挂载之前调用,相关render函数首次被调用
  • mounted:了被新创建的vm.$el替换,并挂载到实例上去之后调用改钩子。
  • beforeUpdate:数据更新前调用,发生在虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例仍然可用。
  • destroyed:实例销毁之后调用,调用后,Vue实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期内部可以做什么?

  • created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。
  • mounted:实例已经挂载完成,可以进行一些DOM操作。
  • beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:可以执行依赖于DOM的操作,但是要避免更改状态,可能会导致更新无线循环。
  • destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。

ajax放在哪个生命周期?:一般放在mounted中,保证逻辑统一性,因为生命周期是同步执行的,ajax是异步执行的。单数服务端渲染ssr同一放在created中,因为服务端渲染不支持mounted方法。什么时候使用beforeDestroy?:当前页面使用$on,需要解绑事件。清楚定时器。解除事件绑定,scroll mousemove

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 的功能所调用的方法)

(学习视频分享:web前端开发编程基础视频

以上是2023年vue高頻面試題分享(附答案分析)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!