一. Vue3与Vue2 diff算法对比
总结:Vue 3 的 diff 算法通过静态树提升、Fragment 支持、Patch Flag、事件处理函数缓存和 Block Tree 等优化,显著提升了性能,减少了不必要的 DOM 操作。
Vue 3 和 Vue 2 的 diff 算法在核心思想上相似,都基于虚拟 DOM 和双端比较,但 Vue 3 在性能和细节上做了优化。以下是主要区别:
1. 静态树提升
- Vue 2:每次重新渲染时,所有节点都会进行比较,包括静态节点。
- Vue 3:通过静态树提升,标记静态节点并在后续渲染中复用,减少不必要的比较。
2. Fragment 支持
- Vue 2:每个组件必须有一个根节点,多个根节点需包裹在一个父节点中。
- Vue 3:支持 Fragment,允许组件有多个根节点,减少了不必要的包装元素。
3. Patch Flag
- Vue 2:每次更新时,所有节点属性都会进行比较。
- Vue 3:引入 Patch Flag,标记动态绑定的属性,更新时只比较这些属性,提升性能。
4. 缓存事件处理函数
- Vue 2:每次重新渲染时,事件处理函数会重新生成。
- Vue 3:缓存事件处理函数,避免重复生成,减少不必要的更新。
二. Vue 3 和 Vue 2 的主要区别是什么?
-
答案要点:
- 性能优化:Vue 3 通过 Proxy 替代
Object.defineProperty实现响应式,性能更好。 - Composition API:Vue 3 引入了 Composition API,提供了更灵活的代码组织方式。
- Tree-shaking 支持:Vue 3 的模块化设计支持 Tree-shaking,减少打包体积。
- Fragment 支持:Vue 3 支持多根节点组件。
- Teleport 组件:Vue 3 新增
<teleport>组件,用于将内容渲染到 DOM 的其他位置。 - 静态树提升:Vue 3 优化了虚拟 DOM 的 diff 算法,提升了渲染性能。
-
- 性能优化:Vue 3 通过 Proxy 替代
三. Vue 3 的响应式原理是什么?
-
答案要点:
- Vue 3 使用 Proxy 实现响应式,而 Vue 2 使用
Object.defineProperty。 - Proxy 可以直接监听对象和数组的变化,无需递归遍历对象属性。
- Proxy 支持监听动态添加的属性,而
Object.defineProperty需要显式调用Vue.set。
- Vue 3 使用 Proxy 实现响应式,而 Vue 2 使用
四. Composition API 是什么?它解决了什么问题?
-
答案要点:
-
Composition API 是 Vue 3 引入的一种新的代码组织方式,核心是
setup函数。 -
解决的问题:
- 在 Vue 2 中,Options API 会导致逻辑分散在
data、methods、computed等选项中,难以维护。 - Composition API 允许将相关逻辑集中在一起,提升代码的可读性和可维护性。
- 在 Vue 2 中,Options API 会导致逻辑分散在
-
核心函数:
ref:用于创建响应式数据。reactive:用于创建响应式对象。computed:用于创建计算属性。watch:用于监听数据变化。
-
五. Vue 3 的 Teleport 组件有什么作用?
-
答案要点:
-
Teleport 组件用于将组件的内容渲染到 DOM 的其他位置。
-
典型应用场景:模态框(Modal)、通知(Toast)等需要脱离当前组件层级的情况。
-
示例:
<teleport to="body"> <div class="modal">这是一个模态框</div> </teleport>
-
六. Vue 3 的 Fragment 是什么?
-
答案要点:(vue实例挂载的DOM元素)
-
Vue 3 支持多根节点组件,称为 Fragment。
-
在 Vue 2 中,组件必须有一个根节点,而 Vue 3 允许组件有多个根节点。
-
示例:
<template> <div>节点 1</div> <div>节点 2</div> </template>
-
七. Vue 3 的 Tree-shaking 是什么?它有什么优势?
-
答案要点:
- Tree-shaking 是一种通过静态分析移除未使用代码的优化技术。
- Vue 3 的模块化设计支持 Tree-shaking,可以减少打包体积。
- 优势:减少最终打包文件的体积,提升加载性能。
八. Vue 3 的 Suspense 组件有什么作用?
-
答案要点:
-
Suspense 组件用于处理异步组件的加载状态。
-
可以在异步组件加载完成前显示一个 fallback 内容(如加载动画)。
-
示例:
<template> <Suspense> <template #default> <AsyncComponent /> </template> <template #fallback> <div>Loading...</div> </template> </Suspense> </template>
-
九. Vue 3 的 v-model 和 Vue 2 有什么区别?
-
答案要点:
-
Vue 2 中,
v-model是v-bind:value和v-on:input的语法糖。 -
Vue 3 中,
v-model可以绑定多个值,并且默认使用modelValue和update:modelValue。 -
示例:
html
复制
<!-- Vue 2 --> <input v-model="message" /> <!-- Vue 3 --> <input v-model="message" /> <input v-model:title="pageTitle" />
-
十. Vue 3 的响应式 API 有哪些?
-
答案要点:
ref:用于创建基本类型的响应式数据。reactive:用于创建对象的响应式数据。computed:用于创建计算属性。watch和watchEffect:用于监听数据变化。
十一. Vue 3 的性能优化有哪些?
-
答案要点:
- 响应式系统优化:使用 Proxy 替代
Object.defineProperty。 - 虚拟 DOM 优化:引入 Patch Flag 和静态树提升。
- Tree-shaking 支持:减少打包体积。
- Composition API:提升代码组织和复用性。
- 响应式系统优化:使用 Proxy 替代
十二. Vue 3 的全局 API 有哪些变化?
-
答案要点:
-
Vue 3 的全局 API 改为按需导入,例如:
import { createApp, ref, reactive } from 'vue'; -
移除了
Vue.extend和Vue.nextTick,改为createApp和nextTick。
-
十三. WebSocket | 背景 概念 原理 使用 优缺点及适用场景
- 答案要点:
- 在 WebSocket 出现之前,为了实现推送技术,所用的技术都是轮询,轮询是指浏览器每隔一段时间向服务器发出 HTTP 请求,服务器再返回最新的数据给客户端
- WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,它使得客户端和服务器之间的数据交换变得更加简单,只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输,本质上一种计算机网络应用层的协议,用来弥补 HTTP 协议在持久通信能力上的不足
十四 模块化-ES Module(ES6 Module)
-
特点:
- 是 JavaScript 的官方模块化标准。
- 支持静态分析,适合 Tree Shaking。
- 使用
import导入模块,export导出模块。
-
模块化的优势:
-
代码复用:模块可以被多次复用,减少重复代码。
-
依赖管理:模块之间的依赖关系清晰,便于维护。
-
作用域隔离:每个模块有自己的作用域,避免全局变量污染。
-
静态分析:ES Module 支持静态分析,适合 Tree Shaking 优化。
十五 动态路由?
- 要在路由配置里设置
meta属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转 - 根据用户登录的账号,返回用户角色
- 前端再根据角色,跟路由表的
meta.role进行匹配 - 把匹配搭配的路由形成可访问的路由
十六 Vuex的响应式处理?
当触发事件的时候,会通过 dispatch 来访问 actions 中的方法,actions 中的 commit 会触发 mutations 中的方法从而修改state 里的值,通过 getter把数据更新到视图
十七 为什么在 vue3 项目开发中使用 v-for 需要指定 key 值?
在 vue3 中,使用 v-for 渲染列表时需要为每个项指定唯一的 key 值。这是因为 vue3 使用了虚拟 DOM 技术,通过比较新旧虚拟节点来进行高效的重新渲染。指定 key 值可以帮助 vue3 更准确地追踪每个节点的变化,减少不必要的重新渲染,提高性能。
十八 在Vue 2中,如果你需要在对象未完全初始化完成之前向其添加属性,有几种方法可以实现。
- 使用Vue的
$set方法 - 使用计算属性或方法返回新的对象或属性值
十九 原型链
1. 什么是原型链?
- 原型链是 JavaScript 中实现继承的机制。每个对象都有一个
[[Prototype]]属性,指向它的原型对象。当访问一个对象的属性或方法时,如果对象本身没有该属性或方法,JavaScript 会沿着原型链向上查找。
2. prototype 和 __proto__ 的区别是什么?
prototype是函数特有的属性,指向原型对象。__proto__是对象特有的属性,指向它的原型对象。
3. 如何实现继承?
-
通过
Object.create或new关键字实现原型链继承。 -
示例:
function Parent() {} function Child() {} Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
4. 如何判断一个对象是否在另一个对象的原型链上?
-
使用
instanceof或Object.prototype.isPrototypeOf。 -
示例:
console.log(child instanceof Parent); // true console.log(Parent.prototype.isPrototypeOf(child)); // true
5. 如何获取对象的原型?
-
使用
Object.getPrototypeOf。 -
示例:
const proto = Object.getPrototypeOf(obj);
6. 如何设置对象的原型?
-
使用
Object.setPrototypeOf。 -
示例:
Object.setPrototypeOf(child, Parent.prototype);
二十 nextTick原理
Vue 3 的 nextTick 是一个用于在下次 DOM 更新循环结束后执行延迟回调的工具。它的原理主要涉及 Vue 的响应式系统和异步更新机制。
1. 响应式系统
Vue 3 的响应式系统基于 Proxy,当数据变化时,Vue 会触发组件的重新渲染。然而,Vue 并不会立即更新 DOM,而是将这些更新任务放入一个队列中,等待合适的时机批量执行。
2. 异步更新队列
Vue 使用异步更新队列来优化性能。当数据变化时,Vue 会将需要更新的组件放入一个队列中,并在下一个事件循环中批量处理这些更新。这样可以避免频繁的 DOM 操作,提升性能。
3. nextTick 的实现
nextTick 的实现依赖于 JavaScript 的事件循环机制。Vue 会优先使用微任务(如 Promise)来执行回调,如果当前环境不支持微任务,则会降级使用宏任务(如 setTimeout)。
主要步骤:
- 收集回调:当调用
nextTick时,Vue 会将传入的回调函数放入一个队列中。 - 调度执行:Vue 会尝试使用微任务(如
Promise.resolve().then())来调度这些回调的执行。如果微任务不可用,则使用宏任务(如setTimeout)。 - 执行回调:在下一个事件循环中,Vue 会依次执行队列中的回调函数。
4. 代码示例
javascript
复制
import { nextTick } from 'vue';
nextTick(() => {
console.log('DOM 更新完成');
});
5. 使用场景
- DOM 更新后操作:当你需要在 Vue 更新 DOM 后执行某些操作时,可以使用
nextTick。 - 等待异步数据:当你需要等待异步数据加载完成后操作 DOM 时,可以使用
nextTick。
6. 源码解析
Vue 3 的 nextTick 实现主要位于 core/src/scheduler.ts 文件中。核心逻辑如下:
typescript
复制
const callbacks: Function[] = [];
let pending = false;
function flushCallbacks() {
pending = false;
const copies = callbacks.slice(0);
callbacks.length = 0;
for (let i = 0; i < copies.length; i++) {
copies[i]();
}
}
let timerFunc;
if (typeof Promise !== 'undefined') {
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
};
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
export function nextTick(callback?: () => void): Promise<void> {
return new Promise((resolve) => {
callbacks.push(() => {
if (callback) callback();
resolve();
});
if (!pending) {
pending = true;
timerFunc();
}
});
}
总结
nextTick 是 Vue 3 中用于在 DOM 更新后执行回调的机制。它利用 JavaScript 的事件循环机制,优先使用微任务来调度回调的执行,确保在 DOM 更新完成后执行用户代码。理解 nextTick 的原理有助于更好地掌握 Vue 的响应式系统和异步更新机制。
二十一. 事件循环常见面试题
1. 什么是事件循环?
- 事件循环是 JavaScript 处理异步任务的机制,通过不断检查调用栈和任务队列来决定任务的执行顺序。
2. 宏任务和微任务的区别是什么?
- 宏任务包括
setTimeout、setInterval等,每次事件循环执行一个宏任务。 - 微任务包括
Promise、MutationObserver等,每次事件循环会清空所有微任务。 - 微任务的优先级高于宏任务。
3. setTimeout(fn, 0) 和 Promise.resolve().then(fn) 的执行顺序是什么?
Promise.resolve().then(fn)是微任务,优先于setTimeout(fn, 0)的宏任务执行。
4. 以下代码的输出顺序是什么?
javascript
复制
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
-
输出顺序:
复制
Start End Promise setTimeout
5. 如何理解 JavaScript 的单线程模型?
- JavaScript 是单线程的,一次只能执行一个任务。通过事件循环机制,它可以高效地处理异步任务,避免阻塞主线程。
6. async/await 和事件循环的关系是什么?
async/await是基于Promise的语法糖,await后面的代码相当于Promise的微任务。
7. 如何优化事件循环的性能?
- 减少同步任务的执行时间。
- 避免长时间运行的微任务阻塞主线程。
- 使用
Web Workers处理耗时任务。