【Vue2 + Vue3】前端八股文面试题
Vue 框架
整理不易,小的快yue过去了,家人们不想开通 VIP ,可以私我,(两元购 PDF版)有更新都会踢。
如果觉得还不错,可以订阅专栏。
【狗头保命,小的认真总结的,大部分都是我面试遇到的】
更新日志
2024年8月30日更新 —— Vue 的内置指令有哪些?(增加了 v-slot
)
2024年8月31日更新 ——
- Vue中,computed和watch一般会在什么情况下使用?它们有什么区别?
- Vue 和 React 有什么不同?
- 增加 子组件向子孙组件传值
文章目录
- Vue 框架
-
- 更新日志
-
- 1.Vue的基本原理?
-
-
- 1)数据驱动视图
- 2)组件化开发
- 3)模板语法
- 4)单文件组件(SFC)
- 5)路由和状态管理
-
- 2.说一说 Vue 双向数据绑定的原理
-
-
- 1)数据劫持
- 2)发布者-订阅者模式
- 3)虚拟 DOM 和 diff 算法
-
- 3. `v-if` 和 `v-show` 的区别?
-
-
- 1)共同点:
- 2)区别:
-
- 4. 为什么 Vue 组件实例对象中的 data 属性是一个函数,而不是一个对象?
- 5.简述一下 Vue 实例挂载的过程
-
-
- 1)创建 Vue 实例
- 2)编译模板
- 3)挂载前钩子函数调用
- 4)创建真实 DOM 并替换占位元素
- 5)挂载完成
-
- 6.说说你对Vue生命周期的理解?
-
-
- 1)主要的生命周期阶段和钩子函数
- 2)生命周期的应用场景
-
- 7. Vue组件间通信的方式有哪些?
-
-
- 1)父组件向子组件通信
- 2)子组件向父组件通信
- 3)兄弟组件之间通信
- 4)祖孙组件之间通信
- 5)子组件向子孙组件传值
-
- 8. Vue中的$nextTick有什么作用?
-
-
- 1)操作更新后的 DOM
- 2)在组件更新后执行某些操作
- 3)解决异步操作与 DOM 操作的顺序问题
-
- 9.为什么 Vue中 `v-if `和 `v-for` 不建议一起用?
-
-
- 1)性能问题
- 2)优先级问题与可维护性
- 3)更好的替代方案
-
- 10. 对Vue中 keep-alive的理解
-
-
- 1)主要功能
- 2)生命周期钩子函数的变化
- 3)应用场景
-
- 11.Vue中给对象添加新属性时,界面不刷新怎么办?
-
-
- 1)使用 Vue.set() 方法
- 2)使用 $set 实例方法
- 3)使用扩展运算符创建新对象
-
- 12.对 mixin的理解?有哪些应用场景?
-
-
- 1)理解
- 2)应用场景
-
- 13.对 slot 的理解,它的使用场景有哪些?
-
-
- 1)理解
- 2)使用场景
-
- 14. Vue中组件和插件有什么区别?
-
-
- 1)定义和功能
- 2)使用方式
- 3)开发和封装
- 4)作用范围
- 5)示例场景
-
- 15. 简要说说,Vue2 和 Vue3的区别?
-
-
- 1)性能优化
- 2)语法和 API 变化
- 3)其他变化
-
- 16.Vue 的内置指令有哪些?
- 17.Vue 虚拟 DOM 中 Diff 算法比较新旧 vnode 的过程
- 18.Diff 算法的原理
-
-
- 1)整体流程
- 2)子节点比较策略
- 3)示例说明
-
- 19. VueX 状态管理器
-
-
- 1) State(状态):
- 2)Getters(获取器):
- 3)Mutations(变更):
- 4)Actions(动作):
- 5)Modules(模块):
-
- 20.如何在vue中设置一个全局方法?
-
-
- 1)方法一:使用 Vue.prototype
- 2)方法二:创建一个全局工具函数模块
- 3)方法三:使用提供者插件(Webpack的ProvidePlugin)
-
- 21.vueUse()为什么能够将方法注册到全局?
-
-
- 1)利用 Vue 的插件机制
- 2)在插件安装过程中进行全局注册
- 3)实现原理总结
-
- 22. Vue中,computed和watch一般会在什么情况下使用?它们有什么区别?
-
-
- 1)`computed`的使用情况
- 2)`watch`的使用情况
- 3)区别
-
- 23.Vue 和 React 有什么不同?
-
-
- 1)设计理念
- 2)语法和模板
- 3)状态管理
- 4)生态系统和社区支持
-
1.Vue的基本原理?
Vue.js 是一个渐进式 JavaScript
框架,它的核心原理主要包括以下几个方面:
1)数据驱动视图
- 响应式系统
- Vue 通过数据劫持结合发布者-订阅者模式,实现了数据的响应式更新。当数据发生变化时,Vue 能够自动更新与之相关的视图部分,而无需手动操作 DOM。
- Vue 使用
Object.defineProperty()
(在 Vue 2.x 中)或Proxy
(在 Vue 3.x 中)来对数据进行劫持。当数据被读取或修改时,Vue 可以追踪到这些操作,并触发相应的更新。
- 虚拟 DOM
- Vue 在更新视图时,不是直接操作真实 DOM,而是先创建一个虚拟 DOM(Virtual DOM)树。虚拟 DOM 是一个轻量级的
JavaScript
对象树,它与真实 DOM 结构相对应。 - 当数据发生变化时,Vue 会比较新旧虚拟 DOM 树的差异,然后只对发生变化的部分进行实际的 DOM 操作,从而提高性能。
- Vue 在更新视图时,不是直接操作真实 DOM,而是先创建一个虚拟 DOM(Virtual DOM)树。虚拟 DOM 是一个轻量级的
2)组件化开发
- 组件定义
- Vue 允许将用户界面拆分成独立的、可复用的组件。每个组件都有自己的模板、逻辑和样式,可以独立开发、测试和维护。
- 组件可以接收输入属性(
props
),并通过自定义事件($emit
)向父组件传递数据。
- 组件生命周期
- Vue 组件有一系列的生命周期钩子函数,在组件的不同阶段被调用。例如,
created
钩子在组件实例被创建后调用,mounted
钩子在组件被挂载到 DOM 后调用。 - 开发者可以在这些钩子函数中执行特定的逻辑,如数据初始化、DOM 操作、订阅事件等。
- Vue 组件有一系列的生命周期钩子函数,在组件的不同阶段被调用。例如,
3)模板语法
- 插值表达式
- Vue 使用双大括号(
{ {}}
)进行插值表达式,在模板中显示数据。例如,<p>{ { message }}</p>
会将数据对象中的message
属性的值显示在<p>
标签中。
- Vue 使用双大括号(
- 指令
- Vue 提供了一系列指令来操作 DOM。例如,
v-if
和v-for
指令可以根据条件显示或循环渲染 DOM 元素。 - v-model 指令实现了表单元素和数据的双向绑定,当表单元素的值发生变化时,数据也会相应更新;反之,当数据发生变化时,表单元素的值也会更新。
- Vue 提供了一系列指令来操作 DOM。例如,
4)单文件组件(SFC)
- 结构
- Vue 支持单文件组件格式,将一个组件的模板、逻辑和样式写在一个文件中,以
.vue
扩展名结尾。 - 单文件组件使得代码结构更加清晰,易于维护和复用。
- Vue 支持单文件组件格式,将一个组件的模板、逻辑和样式写在一个文件中,以
- 编译过程
- 在开发过程中,需要使用构建工具(如 Webpack、Vite 等)对单文件组件进行编译。构建工具会将
.vue
文件中的模板、逻辑和样式分别编译成 JavaScript 代码,然后打包成最终的应用程序。
- 在开发过程中,需要使用构建工具(如 Webpack、Vite 等)对单文件组件进行编译。构建工具会将
5)路由和状态管理
- 路由(Vue Router)
- Vue Router 是 Vue 的官方路由管理器,用于实现单页应用(SPA)的页面导航。它允许开发者定义不同的路由路径,并将对应的组件渲染到页面中。
- Vue Router 支持动态路由、嵌套路由等功能,可以满足复杂的应用需求。
- 状态管理(Vuex)
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它集中管理应用的所有状态,并提供了一种可预测的方式来更新状态。
- Vuex 包含 state(状态)、mutations(更改状态的方法)、actions(异步操作)、getters(计算属性)、module(模块)等概念,帮助开发者更好地组织和管理应用的状态。
2.说一说 Vue 双向数据绑定的原理
Vue 的双向数据绑定主要是通过以下几个核心技术实现的:
1)数据劫持
在 Vue 2.x 中,利用 Object.defineProperty()
方法来实现数据劫持。这个方法可以为对象的属性添加 getter 和 setter,当属性被读取或修改时,可以触发相应的操作。
例如:
const obj = {
};
Object.defineProperty(obj, 'property', {
enumerable: true,
configurable: true,
get: function () {
console.log('Getting property');
return value;
},
set: function (newValue) {
console.log(`Setting property to ${
newValue}`);
value = newValue;
}
});
Vue 遍历数据对象的所有属性,为其添加 getter 和 setter,这样当数据发生变化时,Vue 可以追踪到这个变化。
2)发布者-订阅者模式
- Dep(依赖收集器)和 Watcher(观察者):
- Vue 在数据劫持的过程中,会为每个属性创建一个 Dep 对象,Dep 负责收集依赖于该属性的 Watcher。
- Watcher 则代表一个具体的依赖,比如一个计算属性或者一个模板中的插值表达式。
- 数据变化触发更新:
- 当数据发生变化时,会触发属性的 setter,在 setter 中通知 Dep 去通知所有的 Watcher 进行更新。
- Watcher 收到更新通知后,会触发自身的回调函数,从而更新相关的视图。
3)虚拟 DOM 和 diff 算法
- 虚拟 DOM:
- Vue 在内存中维护了一份虚拟 DOM 树,它是真实 DOM 的轻量级描述。
- 当数据发生变化时,Vue 会先更新虚拟 DOM,而不是直接操作真实 DOM。
- diff 算法:
- Vue 会使用 diff 算法比较新旧虚拟 DOM 的差异,只对发生变化的部分进行实际的 DOM 操作,从而提高性能。
例如:
<div id="app">
<p>{
{ message }}</p>
</div>
const app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
当 message
的值发生变化时,Vue 会通过数据劫持检测到这个变化,然后通知相关的 Watcher 更新虚拟 DOM。接着,通过 diff 算法找到需要更新的真实 DOM 节点,并进行更新操作。
在 Vue 3.x 中,使用了 Proxy
来替代 Object.defineProperty()
进行数据劫持,它能够更全面地监听对象的变化,并且性能更好。原理基本类似,但实现方式有所不同。
3. v-if
和 v-show
的区别?
1)共同点:
通过条件控制元素的显示和隐藏。
2)区别:
v-if
是一个“惰性”指令- 在编译时刻,Vue会根据条件决定是否编译或挂载元素到 DOM 中,为
false
时,元素不会被挂载到 DOM; - 在条件切换时,会涉及到 DOM 的删除和重新插入,可能会有一定开销;
- 在初始渲染时,若条件为
false
,元素不会渲染到 DOM 中,有一定性能优势。
- 在编译时刻,Vue会根据条件决定是否编译或挂载元素到 DOM 中,为
v-show
是一个“非惰性”指令- 在编译时刻,元素总会被渲染到 DOM 中。会通过 CSS 控制元素的显示和隐藏,不会从 DOM 中移除;
- 在条件切换时,只是通过 CSS ,不会销毁和重新创建元素,因此开销较小;
- 在初始渲染时,总是会被渲染到 DOM 中,会有一些额外的开销。
4. 为什么 Vue 组件实例对象中的 data 属性是一个函数,而不是一个对象?
- 根实例对象(它是一个单例) data 可以是函数
()
,也可以是对象{}
,不会产生数据污染。 - 组件实例对象 data必须为函数,
- 目的是为了防止多个组件实例对象之间,共用一个 data,产生污染数据;
- 采用函数形式,
initData
时会将其作为工厂函数,返回全新的 data 对象。
5.简述一下 Vue 实例挂载的过程
Vue 实例挂载的过程主要包括以下几个步骤:
1)创建 Vue 实例
当使用 new Vue()
创建一个 Vue 实例时,会进行一系列的初始化操作:
- 选项合并:
- 将传入的选项(如
data
、methods
、computed
、watch
等)与 Vue 的默认选项进行合并。
- 将传入的选项(如
- 数据响应式处理:
- 对
data
选项中的数据进行数据劫持,通过Object.defineProperty()
(在 Vue 2.x 中)或Proxy
(在 Vue 3.x 中)使其成为响应式数据。 - 这意味着当这些数据发生变化时,Vue 能够自动更新相关的视图。
- 对
2)编译模板
- 如果在创建 Vue 实例时指定了
el
选项,Vue 会开始查找对应的 DOM 元素,并进行模板编译。 - 如果使用单文件组件(
.vue
文件)或通过字符串模板的方式,Vue 会在构建阶段进行模板编译,并将编译结果存储起来,以便在实例挂载时使用。
3)挂载前钩子函数调用
- 在挂载实例之前,会调用
beforeMount
生命周期钩子函数。 - 这个钩子函数可以在此时进行一些准备工作,例如加载数据、初始化插件等。
4)创建真实 DOM 并替换占位元素
- Vue 会根据编译后的模板创建真实的 DOM 元素,并将其插入到页面中。
- 如果在创建 Vue 实例时指定了
el
选项,并且该选项对应的 DOM 元素中已经存在内容,Vue 会将其替换为新创建的 DOM 元素。 - 如果使用了路由或动态组件,Vue 会根据当前的路由或组件状态来决定要挂载的组件,并进行相应的 DOM 操作。
5)挂载完成
- 当 DOM 操作完成后,Vue 会调用
mounted
生命周期钩子函数。 - 此时,实例已经被挂载到 DOM 上,可以在这个钩子函数中进行一些与 DOM 操作相关的初始化工作,例如获取 DOM 元素、初始化第三方库等。
- Vue 会建立起数据与视图之间的双向绑定关系,当数据发生变化时,会自动更新视图;
- 同时,当用户在视图上进行交互操作时,也会自动更新数据。
例如:
<div id="app">
<p>{
{ message }}</p>
</div>
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
beforeMount() {
console.log('Before mount');
},
mounted() {
console.log('Mounted');
}
});
在这个示例中,Vue 实例的挂载过程会依次输出 Before mount
和 Mounted
,表示在挂载前和挂载完成时调用了相应的生命周期钩子函数。
6.说说你对Vue生命周期的理解?
Vue 的生命周期是指一个 Vue 实例从创建到销毁的整个过程中所经历的一系列阶段,每个阶段都有特定的钩子函数可以让开发者在相应的时刻执行自定义的逻辑。
1)主要的生命周期阶段和钩子函数
- 创建阶段:
beforeCreate
:在实例初始化之后,数据观测和事件配置之前被调用。此时,实例的选项对象还未创建完成,data
、methods
、computed
等属性还不可用。created
:在实例创建完成后被立即调用。此时,实例的数据观测、事件和方法已经配置完成,可以访问到data
、methods
等属性,但 DOM 还未挂载,无法访问到真实的 DOM 元素。
- 挂载阶段:
beforeMount
:在挂载开始之前被调用。此时,模板已经编译完成,但真实的 DOM 还未渲染。mounted
:在挂载完成后被调用。此时,真实的 DOM 已经渲染完成,可以访问到真实的 DOM 元素,可以进行 DOM 操作和初始化第三方库等。
- 更新阶段:
beforeUpdate
:在数据更新时,但在虚拟 DOM 重新渲染和打补丁之前被调用。可以在这个钩子中获取更新前的状态。updated
:在数据更新导致的虚拟 DOM 重新渲染和打补丁之后被调用。可以在这个钩子中获取更新后的状态,但要注意避免在这个钩子中进行数据的更改,以免陷入无限循环。
- 销毁阶段:
beforeDestroy
:在实例销毁之前被调用。可以在这个钩子中进行一些清理操作,如解绑事件、清除定时器等。destroyed
:在实例销毁后被调用。此时,实例的所有指令、监听器和子实例都已经被销毁。
2)生命周期的应用场景
- 数据初始化:在
created
钩子中可以进行数据的初始化操作,如从服务器获取数据、初始化计算属性等。created() { // 从服务器获取数据 this.fetchDataFromServer(); },
- DOM 操作:在
mounted
钩子中可以进行 DOM 操作,如获取 DOM 元素、初始化第三方库等。mounted() { const element = document.getElementById('myElement'); // 对 element 进行操作 },
- 数据更新处理:在
updated
钩子中可以处理数据更新后的逻辑,如重新计算某些值、更新图表等。但要注意避免在这个钩子中再次更改数据,以免引起无限循环。updated() { // 处理数据更新后的逻辑 },
- 清理操作:在
beforeDestroy
钩子中可以进行一些清理操作,如解绑事件、清除定时器、取消订阅等。beforeDestroy() { // 解绑事件 window.removeEventListener('resize', this.handleResize); // 清除定时器 clearInterval(this.timer); },
7. Vue组件间通信的方式有哪些?
1)父组件向子组件通信
props
属性传递:- 子组件通过在
props
选项中声明需要接收的属性, - 父组件在使用子组件时通过属性绑定的方式将数据传递给子组件。
- 子组件通过在
例如:
<!-- 父组件 -->
<template>
<child-component :message="parentMessage"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
parentMessage: '这是父组件的数据',
};
},
};
</script>
<!-- 子组件 -->
<template>
<p>{
{ message }}</p>
</template>
<script>
export default {
props: ['message'],
};
</script>
2)子组件向父组件通信
- 自定义事件:
- 子组件通过
$emit
方法触发自定义事件,并传递数据给父组件。 - 父组件在使用子组件时通过监听子组件的自定义事件来接收数据。
- 子组件通过
例如:
<!-- 父组件 -->
<template>
<child-component @custom-event="handleChildEvent"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
methods: {
handleChildEvent(data) {
console.log(data); // 这是子组件传递过来的数据
},
},
};
</script>
<!-- 子组件 -->
<template>
<button @click="emitEvent">触发事件</button>
</template>
<script>
export default {
methods: {
emitEvent() {
this.$emit('custom-event', '这是子组件的数据');
},
},
};
</script>
3)兄弟组件之间通信
- 事件总线(Event Bus):
- 创建一个全局的事件总线对象,可以使用 Vue 实例或一个单独的空 Vue 实例。
- 兄弟组件通过在事件总线上触发和监听事件来传递数据。
例如:
// 创建事件总线
const eventBus = new Vue();
// 组件 A
export default {
methods: {
sendDataToBrother() {
eventBus.$emit('custom-event', '这是组件 A 的数据');
},
},
};
// 组件 B
export default {
created() {
eventBus.$on('custom-event', (data) => {
console.log(data); // 这是组件 A 传递过来的数据
});
},
};
- Vuex 状态管理:
- 使用 Vuex 集中管理应用的状态。
- 兄弟组件可以通过在 Vuex 的 actions、mutations 和 getters 中进行操作来实现数据传递。
例如:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
sharedData: '',
},
mutations: {
setSharedData(state, data) {
state.sharedData = data;
},
},
actions: {
updateSharedData(context, data) {
context.commit('setSharedData', data);
},
},
getters: {
getSharedData: (state) => state.sharedData,