面试题[Vue]
1. Vue2 相关
1.1 Vue 中的 data 属性是一个函数而不是一个对象
在 Vue.js 中,data
属性是一个函数而不是一个对象的主要原因是为了确保每个组件实例都有一个独立的状态(即独立的数据作用域)。这样做可以避免由于共享数据对象带来的意外副作用和错误。
- 具体来说,当我们在单页应用中使用多个 Vue 组件时,如果
data
是一个对象而不是函数,那么这些组件将共享同一个数据对象。这意味着对一个组件状态的修改会影响到其他所有引用这个对象的组件,导致难以预测的错误。 - 而将
data
定义为函数,我们可以在每次创建新组件实例时返回一个全新的对象,从而保证每个组件实例的数据都是独立的。 - 组件复用:Vue.js 提倡组件的复用和单一职责。通过使用函数返回新对象的方式,可以确保每次使用组件时都有一个独立的数据作用域。这在大型应用中尤为重要,因为它让我们可以放心地复用组件而不用担心数据污染或冲突。
- 单例组件的特殊情况:我们有时会用到单例组件,比如根实例(new Vue({})),这种情况下我们可以直接将 data 定义为对象,因为它只会实例化一次,不存在多个实例共享数据的问题。不过这个在实际开发中是较少用到的,更多的情况下还是使用函数返回对象。
1.2 计算属性、监听器、方法
在 Vue 中,computed
和 methods
都是用于定义组件逻辑的方法,但它们的用法和目的不同。
- computed:
- 用于基于已有的响应式数据进行计算,返回一个新的值。
- 具有缓存机制,只有当相关依赖的数据发生变化时才会重新计算。
- 适用于需要依赖多个响应式数据进行复杂计算的场景。
- watch:
- methods:
- 用于定义组件的行为或函数,其内的代码在每次调用时都会重新执行。
- 没有缓存机制,无论何时调用都会重新计算和执行。
- 适用于处理事件或触发特定逻辑的场景。
1.3 过滤器
过滤器在Vue中是一个非常简洁而方便的工具,主要用于文本格式化。它们通常用于模板表达式中,将数据进行转换、格式化,使其在视图中表现得更具可读性和用户友好。具体应用场景有:
- 1)日期格式化:将后端返回的时间戳转换为用户可读的日期格式。
- 2)文本格式化:将文字转换为大写、小写或者截取一定长度等。
- 3)数值格式化:对货币、百分比等数值进行格式化显示。
在Vue 3中,过滤器这一特性已经被移除,官方推荐使用方法(methods)或者计算属性(computed properties)来替代。
1.4 生命周期钩子
Vue 的生命周期可以分为8个阶段:创建阶段、挂载阶段、更新阶段和销毁阶段,每个阶段都有对应的钩子函数。
- Vue 的生命周期主要包含以下几个钩子(事件)函数
- 1)beforeCreate:实例初始化之后,数据观测和事件配置之前被调用。
- 2)created:实例已经创建完成,属性已经绑定,但还未挂载到 DOM 上。
- 3)beforeMount:在挂载之前被调用,相关的 render 函数首次被调用。
- 4)mounted:在挂载完成之后被调用,DOM 挂载完成,可以进行 DOM 操作。
- 5)beforeUpdate:在数据更新时,立即调用该函数,发生在虚拟 DOM 重新渲染之前。
- 6)updated:在数据更新导致虚拟 DOM 重新渲染和打补丁之后调用。
- 7)beforeDestroy:实例销毁之前调用。实例仍然完全可用。
- 8)destroyed:实例销毁之后调用。这一步,实例已经脱离了 DOM 结构,绑定的事件处理已经被移除,子实例也已被销毁。
- 第一次加载 Vue 页面时会触发哪些生命周期钩子
- 1)
beforeCreate
:实例初始化之后,数据观测 (data observer) 和事件配置之前被调用。 - 2)
created
:实例已创建完成,数据观测、属性和方法已配置,事件已配置,但未进行 DOM 渲染。 - 3)
beforeMount
:在挂载开始之前被调用:相关的 render 函数首次被调用。 - 4)
mounted
:Vue 实例挂载到实例挂载的 DOM 上后调用。此时,Vue 实例包含了模板中渲染的 DOM 元素。
- 1)
1.5 组件之间的传值方式
- 父子组件
- Props:父组件通过 props 向子组件传递数据。
- 自定义事件:子组件通过 $emit 发送事件到父组件,并附带数据。
- refs:父组件通过 ref 获取子组件实例,然后直接访问子组件的数据或方法。
- 作用域插槽。
- 其他
1.6 v-show 和 v-if 的区别
v-show 和 v-if 是 Vue.js 中两种常用的指令,都可以用于控制元素的显示和隐藏,但它们有本质上的区别。
- 基本区别
- v-show 是通过控制元素的 CSS display 属性来显示或隐藏元素。无论条件是否为真,元素都会被渲染到 DOM 中,只是通过设置 CSS 样式来控制它的可见性。
- v-if 则是通过条件判断来决定是否渲染元素。如果条件为假,元素根本不会被渲染到 DOM 中。
- 使用场景
- v-show 适合用于需要频繁切换显示/隐藏状态的场景。因为它只是在现有的 DOM 元素上进行 CSS 切换,性能开销较小。
- v-if 适合用于在条件变化不太频繁的情况下使用,因为它每次重新渲染时都会进行完整的 DOM 操作,性能开销较大。
- 进一步探讨它们的一些细节和实际应用中的注意事项
- 性能:
- v-show 带来的性能开销主要体现在第一次渲染时。因为即便元素隐藏了,它还是会占据 DOM 的空间和资源。但是,后续的切换开销极小。
- v-if 每次状态切换都伴随着元素的创建和销毁,当条件频繁变化时,这样的操作会带来一定的性能开销。因此,在频繁切换时,不推荐使用 v-if。
- 初始渲染:
- v-show 在初次渲染时无论条件是否满足都会将元素生成到 DOM 中,然后根据条件通过修改 display 属性来决定显示/隐藏。
- v-if 在初次渲染时会根据条件决定是否创建元素,条件为假时,元素不会生成到 DOM 中。
- 使用搭配:
- 针对某些场景,可以考虑 v-show 和 v-if 的结合使用。例如,外层使用 v-if 进行一次性判断是否渲染内容,因为 v-if 可以确保根本不生成不需要的 DOM 元素;内层使用 v-show 进行频繁的显示隐藏切换。
- 过渡效果:
- 在使用过渡效果时,v-show 和 v-if 的行为也有所不同。v-show 会触发 CSS 过渡效果(transition),而 v-if 需要配合 Vue 的 transition 组件使用。
- 开发指南:
- 当你需要确保某个 DOM 元素在结构上存在,但在某些情况下需要隐藏它,建议使用 v-show。
- 当你确定在某些条件下完全不需要某个 DOM 元素时,使用 v-if 会更适合。
- 性能:
1.7 v-cloak 和 v-pre 的作用
v-cloak 和 v-pre 是 Vue 提供的两个专用指令,用于处理应用程序在加载和解析过程中的一些特殊需求。
v-cloak:
- 主要用来防止闪烁,在 Vue 应用程序完全编译之前,将绑定的 DOM 元素上添加一个
display:none
样式,避免未解析的模板直接显示在用户面前,通常和 CSS 一同使用。 - 原理及使用场景:当 Vue 应用程序需要请求外部资源或进行复杂的数据初始化时,页面内容的加载可能存在延迟,导致用户看到未经解析的模板闪现。v-cloak 可以帮助你隐藏这些未解析的模板,保证用户体验的顺畅。实际应用过程中,可以将 v-cloak 属性绑定在需要隐藏的元素上,并通过 CSS 控制显示隐藏。当 Vue 的编译过程解析完这个元素后,v-cloak 自动移除,显示内容。
- 主要用来防止闪烁,在 Vue 应用程序完全编译之前,将绑定的 DOM 元素上添加一个
v-pre:
主要用于跳过该节点及其子节点的编译过程,直接输出原始的 Mustache 标签(插值符号 ),对提高性能有一定帮助。
原理及使用场景:这个指令在需要提升性能或避免干扰的场景下尤为有用。由于 v-pre 指令会告诉 Vue 忽略这个节点及其所有子节点的编译过程,从而减少不必要的计算量。它会渲染出未经编译的模板内容。
vue<div v-pre> {{ rawMustache }} <!-- 这里的内容将会直接输出为 {{ rawMustache }} 而不是进行数据绑定 --> </div>
使用建议
- v-cloak 通常用在应用启动阶段,确保用户不看到“闪烁”的内容。适用于任何 Vue 应用程序。
- v-pre 则适合用于较复杂的模板结构中,可以显著提升性能,防止不必要的干扰。适合大型项目或者需要优化性能的地方。
1.8 v-model
在 Vue 中,v-model 指令可以用于双向数据绑定。当我们在父组件中使用 v-model 绑定一个属性时,Vue 会将该属性及其更新方法自动传递给子组件。
具体来说,v-model 将父组件中的属性和更新方法分别绑定到子组件的 value
属性和 input
事件上。当子组件修改了 value
的值时,会触发 input
事件,并将新的值通过该事件传递给父组件,从而实现了双向数据绑定。
<!-- FatherComponent -->
<template>
<div>
<ChildComponent v-model="data"></ChildComponent>
<p>Parent Component: {{ data }}</p>
</div>
</template>
<!-- ChildComponent -->
<template>
<div>
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</div>
</template>
<script>
export default {
props: ['value']
};
</script>
1.9 nextTick
Vue 中的 nextTick
是一个常用的方法,它的主要作用是在下一次 DOM 更新循环完成之后执行传入的回调函数。简单来说,nextTick
让我们可以在数据变化导致 DOM 更新之后,对新的 DOM 结构进行操作。
使用场景通常是在你想要确保操作的是更新后的 DOM 元素时。比如,你在修改数据之后立即想要获取或操作新插入的 DOM 元素,此时 nextTick
就派上用场了。
this.someData = newValue;
this.$nextTick(() => {
// DOM 已完成更新,可以操作新元素了
this.$refs.someElement.focus();
});
为什么需要
nextTick
?Vue 的响应式数据是异步更新的。Vue 在观察到数据变化时,不会立即更新 DOM,而是开启一个队列,在同一个事件循环中合并所有的数据变化,然后在下一个事件循环中运行实际的 DOM 更新和渲染。在多数场景下,这种方式非常高效,可以避免多次不必要的重复渲染。
但是某些情况下,你需要确保在操作 DOM 之前,所有的数据变更导致的 DOM 更新都已经完成。例如,你在代码中修改了某个数据,然后期望立即在 DOM 中看到更新的结果。如果直接进行 DOM 操作,你容易操作到旧的 DOM 状态,导致逻辑错误。这时
nextTick
能保证视图已经被更新,然后再执行回调函数。使用
nextTick
的注意事项- 只有在 Vue 处理 DOM 更新时(即响应式数据更新)才需要使用
nextTick
。 - 如果直接在 mounted 或 updated 生命周期钩子中操作 DOM,也可以不使用
nextTick
,因为这些钩子是在 DOM 初次渲染或更新之后执行的。
- 只有在 Vue 处理 DOM 更新时(即响应式数据更新)才需要使用
实现原理
nextTick
的实现原理是通过微任务(如 Promise)、宏任务(如 setTimeout) 等技术,来达到在所有同步任务执行完毕之后再执行回调。Vue 会根据当前宿主环境择优选择微任务,有效地提升执行效率。在 Vue3 中的变化
在 Vue3 中,
nextTick
仍然存在,而且其用法也几乎没有变化。但 Vue3 提供了更多的对微任务和异步行为的支持,比如setup
函数、组合式 API 等,让开发者更加灵活地进行异步调度处理。
1.10 scoped
在 Vue 的单文件组件中,scoped
属性用于将样式限定在当前组件模块中,使其仅对当前组件起作用。它是一种 CSS 模块化的技术,有助于避免样式的冲突和影响范围的扩散。
具体而言,scoped
属性会给组件的 <style>
标签添加一个特殊的属性选择器,以确保只有当前组件的元素受到样式的影响。这样,组件中定义的样式规则仅在组件的范围内生效,不会影响到其他组件或全局样式。
1.11 key
1.12 keep-alive
1.13 slot 原理
在 Vue.js 中,slot
是一种用于在组件模板中分发内容的机制。我们可以使用它来将父组件的内容传递给子组件,从而实现灵活的内容分发和组件复用。具体来说,slot
可以帮助我们在子组件中定义占位符,这些占位符将被父组件传递的内容所替换。这使得我们在开发过程中可以创建更具通用性和复用性的组件。
1.14 响应式数组有哪些限制?如何解决这些限制?
1.15 复用已经封装好的组件时,在不修改原组件的前提下,如何扩展功能
在Vue中,可以通过使用混入(Mixin)来扩展已封装好的组件的功能,而无需修改原组件的代码。混入是一种可重用的Vue组件选项对象,可以包含组件的各种选项,如数据、计算属性、方法等。
通过混入,你可以在不修改原组件的情况下,重复使用相同的功能扩展逻辑,并在不同的组件中进行组合和定制化。但要注意,混入可能会引起命名冲突或逻辑复杂性,因此请谨慎使用,并确保正确处理混入的数据和方法与原组件之间的交互关系。
// 1. 创建一个混入对象,其中包含要扩展的功能:
// mixin.js
const myMixin = {
data() {
return {
additionalData: 'This is additional data',
};
},
methods: {
additionalMethod() {
console.log('This is an additional method');
},
},
};
export default myMixin;
// 2. 在需要扩展功能的组件中导入并使用混入对象:
// MyComponent.vue
<template>
<!-- Original component template -->
</template>
<script>
import myMixin from './mixin.js';
export default {
mixins: [myMixin],
// 原组件的选项继续定义...
};
</script>
1.16 计算属性的函数名和 data 中的属性可以同名吗
不可以。因为 Vue 会将 data
中的属性和计算属性都挂载到 Vue 实例上,如果它们同名,则会发生命名冲突,导致实例中的属性被覆盖,从而引发不可预知的错误。
- 命名冲突的本质: 在 Vue 中,
data
和计算属性(computed)最终都会作为 Vue 实例的一个属性存在。如果我们在data
中定义了一个属性foo
,同时又在computed
中定义了一个名为foo
的计算属性,二者会产生命名冲突,Vue 会警告你存在重复定义。Vue 在初始化时会按照一定的顺序(如Props、methods、data、computed、watch
)将这些属性挂载到 Vue 实例上,后挂载的属性会覆盖先挂载的同名属性。 - 计算属性的优先级:
data
的优先级高于计算属性。如果在data
和计算属性中存在相同的属性,那么在模板中使用这些属性时,会优先使用data
中的数据,因为data
是直接存储数据的,而计算属性是基于data
或其他属性的变化进行计算的。 - 避免命名冲突的最佳实践: 为了保持代码的清晰和简洁,建议在项目实施时遵循以下几点:
- 命名规范: 确保
data
和计算属性有不同的命名,避免命名冲突。 - 模块化管理: 将各自逻辑进行分模块管理,提高代码的可读性和可维护性。
- 严格代码审查: 在代码审查阶段注意这些潜在问题,及时纠正。
- 命名规范: 确保
- 命名冲突如何提醒: 在运行环境下,Vue 通常会在控制台输出警告信息,来提醒开发者存在属性命名冲突,帮助快速定位和修复问题。
- 其它Vue中的相关特性:
- methods: 与计算属性类似,
methods
中的方法也会被挂载到 Vue 实例上,同样需要避免与data
和计算属性同名。 - Watchers: 虽然数据监听器(watchers)与
data
和计算属性相关,但它们不会直接参与命名冲突,因为watchers
本身不挂载属性名到 Vue 实例上。
- methods: 与计算属性类似,
1.17 Vue 组件中的 name 选项
在 Vue 组件中定义 name
选项的主要作用是为组件指定一个名字,这个名字在调试、递归组件、全局注册和基础组件复用时会非常有用。具体来看,name
选项会:
调试和工具支持:
- 帮助在 Vue DevTools 中识别组件,增强调试体验。
- 当你在开发过程中使用 Vue DevTools 时,组件名称能让你更清晰地了解组件树结构。如果没有
name
选项,Vue DevTools 中的组件会以匿名组件显示名称,这在调试过程中会增加一定难度。
递归组件:
在递归组件调用中,确保 Vue 能够正确引用自身。
当你想在一个组件内部递归调用自己时,必须给这个组件提供一个
name
。例如,树形结构组件常常需要递归调用自身以渲染嵌套列表。示例代码如下:javascriptconst Tree = { name: 'Tree', // 其他选项 template: ` <ul> <li v-for="node in treeData" :key="node.id"> {{ node.name }} <Tree v-if="node.children" :treeData="node.children"/> </li> </ul> `, props: ['treeData'] };
全局注册和复用:
用于全局组件注册,使得组件能够被全局识别和使用。
给组件命名后,你可以全局注册它并通过名称直接引用,从而提高组件的复用性。例如:
javascriptimport MyComponent from './MyComponent.vue'; Vue.component('MyComponent', MyComponent);
这样你可以在任何地方使用
<MyComponent></MyComponent>
。
<keep-alive>
结合:提高在
<keep-alive>
中使用时的可读性和可调试性。在使用
<keep-alive>
标签时(它用于在组件切换时保存组件的状态或避免重新渲染),你常常会指定名字,以更方便地控制缓存。例如:vue<keep-alive include="MyComponent"> <router-view></router-view> </keep-alive>
1.18 如何保留模板中 HTML 注释
在 Vue 渲染模板时,默认情况下,HTML 注释不会被保留。但你可以通过在 Vue 组件中使用特殊的指令 v-html
来实现保留注释的需求。v-html
是 Vue 提供的一个特殊指令,可以用于直接插入 HTML 内容,包含了 HTML 注释。
具体操作步骤如下:
<template>
<div v-html="htmlContent"></div>
</template>
<script>
export default {
data() {
return {
htmlContent: '<!-- This is an HTML comment -->'
};
}
};
</script>
1.19 在 Vue 组件中访问根实例
在 Vue 组件中,我们可以通过 this.$root
来访问根实例。this.$root
属性指向当前 Vue 实例树的根实例,可以帮助我们在子组件中获取全局状态或者调用根实例的方法。
1.20 在 Vue 中使用 this 时应该注意哪些问题
在 Vue 中使用 this
主要涉及以下几点需要注意的地方:
- 1)
this
的上下文绑定问题,特别是在使用箭头函数和普通函数时,this
的指向会有不同。 - 2)在生命周期钩子函数中,
this
的指向是 Vue 实例。 - 3)在模板内的
this
是隐式的,我们可以直接引用数据和方法,而无需手动绑定this
。 - 4)在组件间通信中,
this
的指向需要特别注意,是父组件还是子组件。
1.21 Vue 的模板语法使用的是哪个 Web 模板引擎
Vue 的模板语法使用的是基于 HTML 的模板引擎。严格来说,Vue 并没有使用一个外部的、预先存在的模板引擎,而是它自身内置了一个模板编译器来处理模板语法。
Vue 的模板语法借鉴了一些传统的模板引擎,但同时也引入了一些独特的特性,这让开发者可以更高效地构建用户界面。
- 1)双花括号语法:在 Vue 模板中,我们使用
双花括号来绑定数据,这被称为 “Mustache” 语法。与很多模板引擎类似,这种语法用来输出普通文本。
- 2)指令系统:Vue 提供了非常强大的指令系统,比如
v-bind
、v-model
、v-if
、v-for
等,通过这些指令可以便捷地将 HTML 表达式与数据绑定。 - 3)事件处理:使用
v-on
指令或@
符号可以非常方便地绑定事件处理器,例如v-on:click
或@click
。 - 4)计算属性和侦听属性:Vue 中支持通过计算属性和侦听属性来处理复杂逻辑,从而保持模板简洁。
- 5)组件系统:Vue 的模板语法非常适合用于其强大的组件系统,通过模板和组件的结合,可以轻松实现复用和抽象。
1.22 定义 Vue 组件模板的方法
在 Vue 中,定义组件模板的方法主要有以下三种:
- 1)字符串模板:在 Vue 组件对象的
template
属性中直接以字符串的形式定义模板。 - 2)单文件组件(SFC):使用
.vue
文件,同时包含<template>、<script>、<style>
三种标签。 - 3)渲染函数(render function):在 Vue 组件的
render
方法中使用 JavaScript 函数来生成虚拟 DOM。
1.23 Vue 的 template 标签
template 主要是作为一个占位符去使用,在 Vue 2 和 Vue 3 中 template 的表现有一些区别:
- Vue 2:作为一个占位符去使用或者是在组件中传递一个插槽内容。无论什么情况,template 在 compiler 后会被去除。
- Vue 3:用法同 Vue 2,但是在不使用 v-if、v-else-if 或 v-else、v-slot、v-for 的时候,Vue 不会进行处理,会直接渲染成一个 HTML 原生的 template 标签。
1.24 Vue 组件模板只有一个根元素
Vue 要求组件模板只能有一个根元素是为了避免多个根元素带来的复杂性和潜在的问题。单根元素帮助组件的递归渲染过程更加简单和高效,确保组件树结构的清晰和一致。
更详细地看一下 Vue 中为什么会有这个限制:
- 1)组件结构的清晰性:当每个组件都有一个根元素时,组件的结构变得更加直观和简单。我们可以很容易地追踪到组件树的层次结构。
- 2)提升性能:单一根元素可以让虚拟DOM的计算和差异化(diffing)算法更加高效。在处理多根元素时,Vue 的 diff 算法会变得更加复杂,可能会带来不必要的性能损失。
- 3)简化管理:当各个组件有明确、单一的根元素时,管理样式和事件绑定变得更加简单。一旦我们有了多个根元素,这些操作将变得难以追踪和维护。
- 4)与任何 DOM 操作工具更好地兼容:像 jQuery 或其他直接操作 DOM 的工具,更期望有一个明确的入口点来进行操作。单根元素能更好地与这些工具兼容。
- 5)组合使用多个组件:当我们在一个父组件中包含多个子组件时,每个子组件都有自己的根元素,能很好地确保各自内容的独立性和隔离性。
- 6)样式作用域:Vue 的 scoped styles 依赖于组件的根元素来限定样式的作用范围,如果没有单一根元素,很难避免样式被误用或影响其他组件。
扩展:从 Vue 2 到 Vue 3 的变化
在 Vue 2 中,模板必须有一个单根元素,否则会报错。然而,在 Vue 3 中引入了 “Fragments” 的概念,这意味着可以拥有多个根元素。不过, 在大多数场景下,建议我们仍然遵循单根元素的最佳实践,因为它能带来更好的代码可读性和维护性。
1.25 如果变量名以 _ 或 $ 开头,会有什么问题
在 Vue 中,如果变量名以 _
或 $
开头,这些变量会被 Vue 框架认为是保留属性,不会作为数据代理到 Vue 实例上,不会出现在 this.$data 对象中。所以,直接通过 this.variableName
无法访问这些变量。
为了访问这些变量,可以通过 this.$data._variableName
或 this.$data.$variableName
的形式访问到这些值。
1.26 Vue 实例的挂载过程
Vue 实例在挂载过程中,主要发生了以下几个步骤:
- 1)初始化:Vue 实例会依次初始化生命周期函数、事件、props、methods、data、computed 和 watch 等选项。
- 2)模板编译:如果提供了
template
选项,则会编译该模板。如果没有,则使用el
属性所指向的 DOM 元素的内容作为模板。 - 3)创建虚拟 DOM:Vue 会根据编译好的模板生成虚拟 DOM。
- 4)数据响应系统:Vue 会使用其响应式系统将数据和 DOM 进行绑定,数据变化时会自动更新 DOM。
- 5)挂载到 DOM:最终,Vue 会将虚拟 DOM 渲染成真实的 DOM 并替换挂载元素。
1.27 Vue 中 :class 和 :style 表示方式
在 Vue 中,:class
和 :style
可以用多种方式进行绑定:
- 1)对象语法:用对象来动态地绑定多个 class 或 style。
- 2)数组语法:通过数组来动态地绑定 class 或 style。
- 3)直接绑定字符串:直接绑定单个 class 名字或 style 字符串。
1.28 v-for 遍历对象时的顺序
在 Vue 中使用 v-for
遍历对象时,遍历的顺序是按照对象的键进行的,它并不保证按插入的顺序进行。实际上,键的顺序是由 JavaScript 引擎决定的。在现代的 JavaScript 引擎中,通常会先遍历整数键,然后遍历字符串键。
为了保证遍历顺序,可以将对象转换成一个数组,用 v-for
遍历这个数组,并在转换过程中按需要的顺序排序。例如,将对象转换为键值对的数组,然后用Array.prototype.sort
方法进行排序。
对象的遍历顺序:
在 JavaScript 中,对象的键是无序的。这意味着不同的 JavaScript 引擎可能会对对象的键以不同的顺序进行遍历。通常,处于下列顺序:
- 整数键按升序排列。
- 之后是字符串键,按照插入的顺序进行排列(但这并不是严格保证的)。
- 最后是符号键,按插入顺序排列。
使用数组确保顺序:
为了明确保证遍历的顺序,我们可以先将对象的键值对转换为数组,然后对数组进行排序。最后在 Vue 中通过
v-for
指令遍历这个排序后的数组。
2. Vue3 相关
2.1 Teleport
2.2 Suspense
2.3 Fragment
2.4 Vue3 中的动画系统有哪些改进
2.5 ref 和 reactive 的区别
2.6 setup 执行背后发生了什么
2.7 setup 为什么可以返回一个 render 函数
3. Vue 拓展知识
3.1 Vue3 和 Vue2 的区别
- 性能提升:
- Vue3:通过改进虚拟 DOM 的算法和底层架构,实现了更快的渲染速度和更低的内存使用率。具体来说,Vue3 的打包大小减少了约41%,初次渲染速度提升了约55%,更新速度快了约133%,内存使用减少了约54%。
- Diff 算法优化、静态标记提升、事件监听缓存、SSR 优化。
- 响应式系统:
- Vue3:使用了基于 Proxy 的响应式系统,可以监听对象的整个属性,包括新增和删除的属性。同时,Vue3 的响应式系统还可以检测数组内部数据的变化。Proxy 可以提供更加细粒度和高效的变化侦测,这使得 Vue 3 在处理响应式数据时更快且更可靠。
- Vue2:使用的是基于 Object.defineProperty 的响应式系统,只能监听某个属性的变化,对于数组内部数据的变化需要特殊处理。
- API 与代码组织:
- Vue3:引入了Composition API,允许开发者使用函数来组织组件逻辑,使得代码更加简洁和可复用。所有 API 都是 import 引入的,对 Tree- shaking 很友好。此外,Vue3 还支持 TypeScript,提供了更好的类型检查和错误提示。
- Vue2:使用的是 Options API,将组件的不同部分(如 data、computed、methods 等)分散在选项中,可能导致代码逻辑不够清晰。Vue2 对 TypeScript 的支持也相对较弱。
- 生命周期钩子:
- Vue3:生命周期钩子函数有所变化,例如
beforeCreate
和created
被setup()
替代,而beforeMount
、mounted
等钩子函数前需要加上on
前缀(如onMounted
)。此外,Vue3 还增加了onRenderTracked
和onRenderTriggered
等新的生命周期钩子。 - Vue2:生命周期钩子函数包括
beforeCreate
、created
、beforeMount
、mounted
等,与 Vue3 有所不同。
- Vue3:生命周期钩子函数有所变化,例如
- 其他特性与变化:
- 新的组件:Fragment、Teleport、Suspense。Fragment: Vue 3 组件不再要求有一个唯一的根节点,清除了很多无用的占位 div。Teleport: 允许组件渲染在别的元素内,主要开发弹窗组件的时候特别有用。Suspense: 异步组件,更方便开发有异步请求的组件。
- 自定义渲染器:Vue 3 利用拆包,使用最近流行的 monorepo 管理方式,响应式、编译和运行时全部独立。
3.2 双向绑定和响应式的关系
双向绑定和响应式是相关但不完全相同的概念。
- 响应式 (Reactivity): 指的是数据的变化能够自动地触发相关依赖的更新,从而保持视图和数据的同步。在 Vue.js 中,响应式系统负责追踪数据的变化,并在数据变化时更新相关的视图,使得视图能够反映出最新的数据状态。响应式系统可以通过监听数据的变化来实现,例如使用 Object.defineProperty() 或者 Proxy。【通过 数据劫持 和 发布订阅模式 实现】
- 双向绑定 (Two-way Binding): 是一种机制(是一种使视图和模型之间保持同步的机制),使得视图和模型之间的变化能够相互影响。当模型数据发生变化时,视图会自动更新以反映这些变化;同时,当视图中的表单元素值发生变化时,模型数据也会自动更新。在 Vue.js 中,双向绑定是通过在模板中使用 v-model 指令来实现的,它能够将表单元素和数据模型之间建立双向关联。
虽然双向绑定通常依赖于响应式系统来实现,但它们并不是完全相同的概念。响应式系统可以单独存在,用于追踪数据的变化并更新相关的视图,而双向绑定则是一种特定的应用场景,旨在实现视图和模型之间的双向同步。
3.3 Composition API 和 Options API 的区别
3.4 Proxy 和 Object.defineProperty 的区别
在Vue中,Proxy
和Object.defineProperty
都用于实现数据的响应式系统,但它们在Vue 2.x和Vue 3.x版本中的应用和区别显著。
- Vue 2.x中的
Object.defineProperty
- 工作方式:
- Vue 2.x使用
Object.defineProperty
在对象的属性上设置getter和setter。 - 当这些属性被访问或修改时,getter和setter会被触发,从而执行相应的逻辑,如更新视图。
- Vue 2.x使用
- 限制:
Object.defineProperty
只能监听对象的属性,不能监听数组的变化(尽管Vue通过重写数组方法实现了对数组变化的监听)。- 对于新添加的属性,
Object.defineProperty
无法自动监听,需要手动调用Vue.set
或使用vm.$set
来确保新属性也是响应式的。 - 在深度嵌套的对象中,
Object.defineProperty
需要递归地应用,这可能会带来性能问题。
- 优势:
- 相对于
Proxy
,Object.defineProperty
通常在性能上更优,因为它直接在对象上定义属性,不需要每次操作都经过代理。
- 相对于
- 工作方式:
- Vue 3.x中的
Proxy
- 工作方式:
- Vue 3.x使用
Proxy
对象来创建一个代理,从而可以拦截对对象属性的读写操作,创建对象的代理。 Proxy
提供了多达13种拦截方法,包括get
、set
、has
、deleteProperty
等,使得Vue 3.x可以更加灵活地处理对象的操作。
- Vue 3.x使用
- 限制:
- 相对于
Object.defineProperty
,Proxy
可能会稍微慢一些,因为每次操作都会经过代理处理。
- 相对于
- 优势:
Proxy
可以监听整个对象或数组,而不仅仅是单个属性。Proxy
可以实时响应对象属性的添加、删除和修改,无需手动调用额外的方法。Proxy
的性能在某些场景下可能更优,特别是对于深度嵌套的对象或数组。
- 处理方式:
- Vue 3.x使用
Proxy
代理对象的第一层,并通过递归地应用reactive
方法来处理深度嵌套的对象,以实现深度观测。 - 对于数组,Vue 3.x通过重写数组的原型方法来拦截对数组的操作,并触发相应的更新逻辑。
- Vue 3.x使用
- 工作方式:
- 总结
- 灵活性:
Proxy
在Vue 3.x中提供了更高的灵活性,可以监听整个对象或数组,并实时响应属性的添加、删除和修改。 - 性能:虽然
Proxy
在某些场景下可能带来性能上的优势,但具体表现取决于应用的复杂性和使用场景。 - 兼容性:
Proxy
是ES6引入的特性,因此Vue 3.x需要较新的JavaScript环境支持。而Object.defineProperty
则具有更好的兼容性,可以在更广泛的浏览器环境中运行。
- 灵活性:
在Vue 3.x中,由于Proxy
提供了更高的灵活性和更好的性能表现,它成为了Vue实现响应式系统的首选方式。然而,在需要兼容旧版浏览器或特定环境下,Vue 2.x及其使用的Object.defineProperty
仍然是一个可行的选择。
3.5 Vue 中使用了哪几种设计模式
3.6 Vue 中的性能优化有哪些常见的技巧
- 使用 v-if 和 v-for 时注意避免不必要的渲染。
- 合理使用 computed 属性和 watch 监听器。
- 使用 keep-alive 组件缓存组件状态。
- 使用异步组件进行按需加载。
- 避免在模板中使用复杂的表达式。
- 使用 key 属性管理组件和元素的复用。
- 合理使用懒加载和分割代码。。。
3.7 对 Vue 的理解?相比于原生开发,使用 Vue 有哪些优点?
Vue 是一个用于构建用户界面的渐进式 JavaScript 框架。它的核心库专注于视图层,易于上手,并且可以与其他库或已有项目集成。Vue 主要通过双向数据绑定、组件化开发和虚拟 DOM 来简化前端开发。
相比于原生开发,使用 Vue 有以下优点:
- 1)双向数据绑定:Vue 提供了一套简洁的双向数据绑定机制,开发者可以轻松实现数据和视图的同步更新,而无需自己去操作 DOM。
- 2)组件化开发:Vue 强调组件化,通过组件可以将 UI 拆分成独立、可复用的部件,使得代码更模块化、可维护。
- 3)虚拟 DOM:Vue 的虚拟 DOM 有效地提升了性能,减少了直接操作 DOM 带来的性能开销。
- 4)渐进式框架:可以根据需要渐进地选择 Vue 的组件和功能,扩展项目规模。
- 5)生态系统丰富:Vue 具有丰富的官方和第三方库,如 Vue Router、Vuex 等,方便实现项目中的常见需求。
3.8 同时使用 v-if 和 v-for 的问题
在 Vue 中同时使用 v-if
和 v-for
可能会导致更高的性能开销和更加复杂的代码维护。原因有以下几点:
- 优先级问题
- 2.x:是 v-for 的优先级高(与 v-if 同时作用在一个元素上时,一般是想渲染列表中某些满足条件的数据,但是这样会导致每次都要遍历全部列表进行判断,我们可以将数据处理完成之后,只渲染满足条件的数据,计算属性就是一个好方法)。
- 3.x:是 v-if 的优先级高(与 v-for 一起作用在同一个元素时,在 v-if 的作用域中是拿不到 v-for 循环中的变量的,而 v-if 中的判断条件一般都是基于 v-for 循环中的变量来判断,所以这个时候会出现错误)。
- 调试和理解困难:同时使用
v-if
和v-for
会使代码更加难以理解和调试,尤其是在复杂的条件和循环中,代码的可维护性和可读性会显著降低。 - 可能出现不必要的渲染:如果在
v-for
循环中使用v-if
,可能会导致出现多次不必要的渲染和销毁,进一步影响性能。 - 优化手段:为了避免上述问题,建议将
v-if
和v-for
分开使用,这样可以提高代码的可读性和性能。通常,可以通过以下两种方式来优化:- 使用计算属性:将
v-if
条件放到计算属性中,只返回符合条件的数组,然后在模板中直接使用v-for
。这样避免了每次迭代中的条件判断。 - 在外层包裹条件判断。
- 使用计算属性:将
3.9 e.target 和 e.currentTarget 的区别
在 Vue 事件中,$event
代表的是原生的 JavaScript 事件对象。通过这个对象,你可以访问事件发生的元素、事件类型等详细信息。而在事件对象中,e.target
和 e.currentTarget
是非常重要的两个属性。
- 1)
e.target
:表示实际触发事件的元素。也就是说,用户点击或操作的那个具体的 DOM 元素。 - 2)
e.currentTarget
:表示事件绑定的元素。也就是说,你在 Vue 模板中用来绑定事件监听器的那个元素。