前端Vue.js面试题(4)
✨✨✨目录
1.Vuex的原理?
2.Vuex和localStorage的区别?
3.Redux和Vuex有什么区别,它们有什么共同思想吗?
4.Vuex中的辅助函数怎么使用?
5.Vue3有什么更新?
6.Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
7.对虚拟DOM的理解?
8.为什么需要虚拟DOM?
9.虚拟DOM一定更快吗?
10.说说Vue中的diff算法?
11.说说Vue中,key的原理?
12.Vue初始化页面闪动问题?
13.extend有什么作用?
1.Vuex的原理?
Vuex是一种状态管理模式,用于管理应用程序的所有组件的状态。存在的目的是共享可复用的组件状态。
Vuex的集中属性及存在的意义:
(1)State(状态):应用程序的数据存储在一个单一的状态树中,即state
。这个状态树是响应式的,当状态发生变化时,相关的组件将自动更新。
(2)Getter(获取器):getter
允许从state
中派生出一些衍生的状态,类似于计算属性。可以使用getter
来对state
进行处理和计算,并将其暴露给组件使用。
(3)Mutation(突变):mutation
是用于修改state
的唯一途径。它定义了一些操作函数,每个函数都有一个特定的名称(称为type
),并且可以在这些函数中改变state
的值。mutation
必须是同步的,以确保状态变更是可追踪的。
(4)Action(动作):action
用于处理异步操作和复杂的业务逻辑。类似于mutation
,但action
可以包含异步操作,可以在action
中触发多个mutation
,也可以在action
中调用其他action
。
(5)Module(模块):为了更好地组织和拆分大型的应用程序,Vuex允许将state
、getter
、mutation
和action
划分为模块。每个模块都有自己的state
、getter
、mutation
和action
,并且可以被嵌套和组合。
2.Vuex和localStorage的区别?
vuex存储在内存中,localstorage以文件的方式存储在本地;vuex用于组件之间传值,localstorage存储在浏览器中,一般是在跨页面传递数据时使用;Vuex能做到数据的响应式,localstorage不能;刷新页面时vuex存储的值会丢失,localstorage不会。
3.Redux和Vuex有什么区别,它们有什么共同思想吗?
相同点:
- state共享数据
- 流程一致:定义全局state,触发,修改state
- 原理相似,通过全局注入store
不同点:
实现原理上来说:
- Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
- Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的
表面层来说:
- vuex定义了state、getter、mutation、action四个对象;redux定义了state、reducer、action。
- vuex中state统一存放,方便理解;reduxstate依赖所有reducer的初始值。
- vuex有getter,目的是快捷得到state;redux没有这层,react-redux mapStateToProps参数做了这个工作。
- vuex中mutation只是单纯赋值(很浅的一层);redux中reducer只是单纯设置新state(很浅的一层)。他俩作用类似,但书写方式不同。
- vuex中action有较为复杂的异步ajax请求;redux中action中可简单可复杂,简单就直接发送数据对象({type:xxx, your-data}),复杂需要调用异步ajax(依赖redux-thunk插件)。
- vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch。
4.Vuex中的辅助函数怎么使用?
在实际开发中,我们经常会用到 vuex 来对数据进行管理,随着数据越来越多,我们逐渐开始使用一些语法糖来帮助我们快速开发。 即 vuex 中的 mapState、mapGetters、mapMutations、mapActions 等辅助函数是我们经常使用到的。
mapState:把state属性映射到computed身上
computed:{...Vuex.mapState({input:state=>state.inputVal,n:state=>state.n})
}
mapAcions:把actions里面的方法映射到methods中
methods:{...Vuex.mapActions({add:"handleTodoAdd", //val为actions里面的方法名称change:"handleInput" })}
mapMutations:把mutations里面的方法映射到methods中
methods:{...Vuex.mapMutations({handleAdd:"handlMutationseAdd"})}
mapGetters:把getters属性映射到computed身上
computed:{...Vuex.mapGetters({NumN:"NumN"})}
modules属性: 模块
把公共的状态按照模块进行划分
- 每个模块都相当于一个小型的Vuex
- 每个模块里面都会有
state
getters
actions
mutations
- 切记在导出模块的时候加一个
namespaced:true
主要的作用是将每个模块都有独立命名空间 namespace:true
在多人协作开发的时候,可能子模块和主模块中的函数名字会相同,这样在调用函数的时候,相同名字的函数都会被调用,就会发生问题。为了解决这个问题,导出模块的时候要加namespace:true
.
5.Vue3有什么更新?
Vue 3相比Vue 2在多个核心领域实现了升级,主要差异如下:
响应式系统重构
Vue 3 采用 Proxy 替代 Vue 2 的Object.defineProperty ,解决了无法检测数组索引/对象新增属性的问题,并优化了深层嵌套对象的性能。
Composition API 引入
通过setup函数替代传统 Options API ,支持逻辑复用与组件拆分,解决了 Vue 2中逻辑分散的问题。
性能优化
- 重写虚拟 DOM 算法(静态标记提升、区块树优化)
- 编译时优化(Tree-shaking技术减少打包体积41%)
- 运行时效率提升
新特性支持
- 多根节点组件 (Fragment)
- 跨组件渲染 (Teleport)
- 异步组件加载状态 (Suspense)
TypeScript 集成
Vue 3源码用 TypeScript 重构,提供更好的类型推断支持,而 Vue 2对TypeScript支持较弱。
生命周期调整
新增 onBeforeUnmount等更语义化的API,兼容 Vue 2生命周期钩子。
6.Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
在 Vue 3.0 中,使用 Proxy API 替代 defineProperty API 是为了改进响应式系统的性能和功能:
(1)性能提升:Proxy API 比 defineProperty API 在许多情况下具有更好的性能。defineProperty 使用 Object.defineProperty 方法来拦截对象属性的访问和修改,但它需要遍历每个属性进行拦截。而 Proxy API 允许拦截整个对象,可以更高效地捕获对对象的访问和修改。
(2)更全面的拦截能力:Proxy API 提供了更多的拦截方法,比 defineProperty API 更灵活、丰富。它支持拦截目标的各种操作,包括读取、设置、删除、枚举等,甚至还可以拦截函数调用和构造函数实例化。
(3)更好的数组变化检测:Vue 3.0 使用 Proxy API 改善了数组的变化检测机制。Proxy 可以直接拦截数组的索引访问和修改,使得对数组的变化更容易被监听到,从而提供了更可靠的响应式行为。
(4)更易于处理嵌套对象:Proxy API 能够递归地拦截对象的嵌套属性,而 defineProperty 无法自动递归处理嵌套对象。这使得在 Vue 3.0 中处理嵌套对象更加简单和方便。
(5)更好的错误提示:相比于 defineProperty,Proxy API 提供了更好的错误追踪和调试信息。当使用 Proxy API 时,如果访问或修改了一个不存在的属性,会直接抛出错误,从而更容易发现和修复问题。
7.对虚拟DOM的理解?
从本质上来说,VirtualDOM是一个JavaScript对象,通过对象的方式来表示DOM结构。将页面的状态抽象为Js对象的形式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将多次DOM修改的结果一次性的更新到页面上,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。
虚拟DOM是对DOM的抽象,这个对象是更加轻量级的对DOM的描述。它设计的最初目的,就是更好的跨平台,比如Node.js就没有DOM,如果想实现SSR,那么一个方式就是借助虚拟DOM,因为虚拟DOM本身是js对象。在代码渲染到页面之前,Vue会把代码转换成一个对象(虚拟DOM)。以对象的形式来描述真实DOM结构,最终渲染到页面。在每次数据发生变化前,虚拟DOM都会缓存一份,变化之时,现在的虚拟DOM会与缓存的虚拟DOM进行比较。在vue内部封装了diff算法,通过这个算法来进行比较,渲染时修改改变的变化,原先没有发生改变的通过原先的数据进行渲染。
另外现代前端框架的一个基本要求就是无须手动操作DOM,一方面是因为手动操作DOM无法保证程序性能,多人协作的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作可以大大提高开发效率。
8.为什么需要虚拟DOM?
DOM
是很慢的,其元素非常庞大,页面的性能问题,大部分都是由DOM
操作引起的真实的DOM
节点,哪怕一个最简单的div
也包含着很多属性,可以打印出来直观感受一下:
由此可见,操作DOM
的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验
举个例子:你用传统的原生api
或jQuery
去操作DOM
时,浏览器会从构建DOM
树开始从头到尾执行一遍流程。
当你在一次操作时,需要更新10个DOM
节点,浏览器没这么智能,收到第一个更新DOM
请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程。
而通过VNode
,同样更新10个DOM
节点,虚拟DOM
不会立即操作DOM
,而是将这10次更新的diff
内容保存到本地的一个js
对象中,最终将这个js
对象一次性attach
到DOM
树上,避免大量的无谓计算。
9.虚拟DOM一定更快吗?
虚拟DOM可以在某些情况下性能,但并不是绝对的。以下是一些虚拟DOM可能带来性能提升的情况:
批量更新:虚拟DOM可以将多个DOM操作合并为一次更新。它会在内部进行比较和计算,找出最小的变更集,并批量应用于真实的DOM树。这种批量更新可以减少浏览器的重排和重绘,从而提高性能。
局部更新:通过比较新旧虚拟DOM树,只有发生变化的部分会被重新渲染到真实的DOM中,而不需要重新渲染整个组件。这可以避免不必要的DOM操作,减少性能开销。
跳过昂贵的计算:在虚拟DOM的比较过程中,可以通过判断节点是否相同来跳过昂贵的计算或渲染步骤。如果两个节点相同,则无需进一步比较其子节点,从而节省了计算资源。
跨平台支持:虚拟DOM与底层平台无关,因此它可以在不同环境(例如浏览器、移动端、服务器端)进行渲染。这种可移植性使得使用虚拟DOM能够更轻松地在不同平台之间共享和重用代码。
然而,虚拟DOM也可能引入一些性能开销:
额外的内存占用:在运行时,虚拟DOM需要维护一个表示整个组件树的数据结构,这可能会占用额外的内存。
操作的复杂度:虚拟DOM需要进行比较、计算和递归遍历等操作,这可能导致一些额外的计算开销。
总的来说,虚拟DOM通常可以在中等到大型规模的应用程序中提供性能优势。然而,在简单的应用程序或特定场景下,手动的DOM操作可能更加高效。因此,在选择是否使用虚拟DOM时,需要权衡应用程序的需求、性能要求以及代码的可维护性。
10.说说Vue中的diff算法?
diff算法是一种通过同层的树节点进行比较的高效算法。diff
算法在很多场景下都有应用,在 vue
中作用于虚拟 dom
渲染成真实 dom
的新旧 VNode
节点比较。
两个特点:
- 比较只会在同层级进行, 不会跨层级比较
- 在diff比较的过程中,循环从两边向中间比较
比较方法:
diff整体策略:深度优先,同层比较
(1)比较只会在同层级进行,不会跨层级比较
(2)比较的过程中,循环从两边向中间收拢
举例说明diff算法更新:
新旧VNode
节点如下图所示:
第一次循环后,发现旧节点D与新节点D相同,直接复用旧节点D作为diff
后的第一个真实节点,同时旧节点endIndex
移动到C,新节点的 startIndex
移动到了 C
第二次循环后,同样是旧节点的末尾和新节点的开头(都是 C)相同,同理,diff
后创建了 C 的真实节点插入到第一次创建的 B 节点后面。同时旧节点的 endIndex
移动到了 B,新节点的 startIndex
移动到了 E
第三次循环中,发现E没有找到,这时候只能直接创建新的真实节点 E,插入到第二次创建的 C 节点之后。同时新节点的 startIndex
移动到了 A。旧节点的 startIndex
和 endIndex
都保持不动
第四次循环中,发现了新旧节点的开头(都是 A)相同,于是 diff
后创建了 A 的真实节点,插入到前一次创建的 E 节点后面。同时旧节点的 startIndex
移动到了 B,新节点的 startIndex
移动到了 B
第五次循环中,情形同第四次循环一样,因此 diff
后创建了 B 真实节点 插入到前一次创建的 A 节点后面。同时旧节点的 startIndex
移动到了 C,新节点的 startIndex 移动到了 F
新节点的 startIndex
已经大于 endIndex
了,需要创建 newStartIdx
和 newEndIdx
之间的所有节点,也就是节点F,直接创建 F 节点对应的真实节点放到 B 节点后面
11.说说Vue中,key的原理?
在Vue中,key是用于帮助Vue识别和跟踪虚拟DOM变化的特殊属性。当Vue更新渲染真实DOM时,它使用key属性来比较新旧节点,并尽可能地复用已存在的真实DOM节点,以提高性能。
Vue 在进行虚拟 DOM 的 diff 算法时,会使用 key
来匹配新旧节点,以确定节点的更新、移动或删除。它通过 key
属性来判断两个节点是否代表相同的实体,而不仅仅是根据它们的内容是否相同。这样可以保留节点的状态和避免不必要的 DOM 操作。
key 的工作原理如下:
- 当Vue更新渲染真实DOM时,它会对新旧节点进行比较,找出它们之间的差异。
- 如果两个节点具有相同的key的值,则Vue认为它们是相同的节点,会尝试复用已存在的真实DOM节点。
- 如果节点具有不同的key的值,Vue会将其视为不同的节点,并进行适当的更新、移动或删除操作。
使用 key
可以提供更准确的节点识别和跟踪,避免出现一些常见的问题,比如在列表中重新排序时导致的元素闪烁、输入框内容丢失等。
key
必须是唯一且稳定的,最好使用具有唯一标识的值,例如使用数据的唯一 ID。同时,不推荐使用随机数作为 key
,因为在每次更新时都会生成新的 key
,导致所有节点都重新渲染,无法复用已有的节点,降低性能。使用index作为key和没写基本上没区别,因为不管数组的顺序怎么颠倒,index都是0,1,2.这样排列,导致Vue会复用错误的旧子节点,做很多额外的工作。
12.Vue初始化页面闪动问题?
使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{message}的字样,虽然一般情况下这个时间很短暂,但是还是有必要让解决这个问题的。
<div v-cloak>{{ message }}
</div>
<style>[v-cloak] {display: none;}
</style>
13.extend有什么作用?
在前端开发中,extend
主要有以下作用:
代码复用与组件扩展
通过extend
可以扩展组件功能或创建可复用的代码模块,例如:
- 组件模板继承:基于现有组件模板扩展新功能,避免重复编写相同代码
- 插件开发:封装常用功能为插件,便于跨项目复用
import Vue from 'vue';
import BaseComponent from './BaseComponent.vue';
const ExtendedComponent = Vue.extend({extends: BaseComponent,data() { return { newProperty: 'This is a new property' } },methods: { newMethod() { console.log('This is a new method'); } }
});
简化CSS维护
在CSS预处理器(如Less)中,extend
用于共享样式代码,避免重复编写相同属性,提升代码整洁性和可维护性。
.button-basic {border: none;padding: 15px 30px;cursor: pointer;
}
.button-report {@extend .button-basic;background-color: red;
}
.button-submit {@extend .button-basic;background-color: green;color: white;
}
对象功能扩展
通过扩展对象属性或方法实现功能增强,例如:
- 合并多个配置对象(如Vue混入mixins)
- 继承基类并添加新功能(如后端开发中的类扩展)
💕💕💕持续更新中......
若文章对你有帮助,点赞❤️、收藏⭐加关注➕吧!