Skip to content

Vue面试题

1、Vue 有了数据响应式,为何还要 diff ?

Vue 中的数据响应式和虚拟 DOM 的 diff 算法是两个不同的概念,它们分别解决了不同的问题,相互协作以提高页面渲染的效率和性能。

数据响应式

Vue 的数据响应式系统通过 Object.defineProperty 或者 ES6 的 Proxy 来实现,主要解决了以下问题:

  1. 数据绑定:保证了视图与数据的同步更新,当数据发生变化时,视图会自动更新,避免了手动操作 DOM 的繁琐和易出错性。
  2. 依赖追踪:Vue 能够追踪每个数据的依赖关系,即哪些组件或者计算属性依赖于某个数据。当数据变化时,自动更新依赖的组件或者计算属性。

虚拟 DOM 和 Diff 算法

虚拟 DOM 是一种内存中的表示结构,它是对真实 DOM 的抽象。Diff 算法是一种高效更新 DOM 的策略,它通过比较新旧虚拟 DOM 树的差异,最小化了更新操作,提高了页面的渲染效率。

为什么还需要 Diff 算法?

  1. 性能优化:直接操作真实 DOM 是非常昂贵的,而虚拟 DOM 可以在内存中快速进行比较和计算差异。Diff 算法帮助减少了更新操作的次数和范围,从而提升了页面渲染的性能。
  2. 批量更新:Diff 算法能够将多次 DOM 更新操作合并为一次,避免了频繁的 DOM 操作,减少了浏览器的重排和重绘。
  3. 跨平台兼容:虚拟 DOM 和 Diff 算法使得 Vue 可以运行在不同的平台上,例如浏览器、Weex 等,统一了渲染逻辑和数据响应式的实现。
  4. 更新效率:即使是响应式系统可以自动更新视图,但是如果每次数据变化都直接操作真实 DOM,可能会带来性能问题。Diff 算法可以智能地比较新旧 DOM 树的变化,只更新必要的部分,从而提高了更新效率。

2:vue3 为什么要引入 Composition API ?

1. 更好的代码组织和重用

在 Vue 2 中,使用选项式 API(Options API)来定义组件的逻辑,通常将数据、方法、计算属性和生命周期钩子分开写在不同的配置对象中。当组件变得复杂时,不同功能的代码可能会散落在各个部分,难以维护和重用。

2. 更好的逻辑复用

在 Vue 2 中,逻辑复用主要通过 mixins 和 scoped slots 实现,但它们都有一些缺点,比如命名冲突和代码可读性差。

Composition API 通过组合函数(composable functions)来实现逻辑复用,这些函数可以在多个组件之间共享和复用逻辑,避免了 mixins 的缺点。

3. 更好的 TypeScript 支持

Composition API 天然地支持 TypeScript,使得类型推断和类型检查更为自然和方便。相比于 Options API,通过 Composition API 定义的逻辑更容易进行类型声明和类型推断,提升了开发体验。

4. 适应函数式编程趋势

Composition API 借鉴了函数式编程的思想,将逻辑封装成函数,使得代码更加简洁、模块化、可测试,同时也更符合现代 JavaScript 开发趋势。

总结

Vue 3 引入 Composition API 主要是为了提升代码组织和复用性、提供更好的 TypeScript 支持、适应函数式编程趋势,并且解决 Vue 2 中存在的一些问题。通过 Composition API,可以让组件逻辑更加清晰、灵活和易于维护。

3、 Vue 事件机制,并手写$on、$off、$emit、$once

Vue 的事件机制允许组件之间进行通信,通过 $on$off$emit$once 等方法进行事件的订阅、取消订阅、触发和一次性订阅。我们可以通过手写这些方法来理解其工作原理。

Vue 事件机制

  1. $on(event, callback):监听特定事件。
  2. $off(event, callback):停止监听特定事件。
  3. $emit(event, ...args):触发特定事件。
  4. $once(event, callback):只监听一次特定事件。

解释

  1. $on:将事件和回调函数添加到 events 对象中。
  2. $off:如果没有传递回调函数,则移除所有监听。如果传递了回调函数,则只移除特定的回调。
  3. $emit:触发事件,调用所有注册的回调函数并传递参数。
  4. $once:使用一个包装函数 (wrapper) 包装原始回调函数,确保回调只执行一次,然后移除事件监听。

4、computed 计算值为什么还可以依赖另外一个 computed 计算值?

在 Vue 中,computed 计算属性可以依赖其他 computed 计算属性,因为 Vue 的响应式系统能够正确地追踪依赖关系。

computed 计算属性的实现

computed 计算属性本质上是具有缓存功能的特殊方法。它们只有在其依赖的响应式属性发生变化时才会重新计算,否则返回缓存的值。

5、说一下 vm.$set 原理

vm.$set 是 Vue 中用于在对象上设置属性并确保新属性是响应式的方法。其实现原理可以简化为以下几个步骤:

  1. 处理数组情况: 如果目标是数组,并且键是有效的数组索引,使用 splice 方法添加新元素以保持响应性。
  2. 处理已有属性: 如果属性已经存在于对象中,直接赋值。
  3. 处理新属性: 如果目标对象不是响应式对象,直接赋值新属性。
  4. 添加响应式新属性: 如果目标对象是响应式的,通过 defineReactive 方法将新属性定义为响应式。这包括定义 getter 和 setter。
  5. 通知依赖更新: 调用 ob.dep.notify() 通知所有依赖于该对象的 watchers 执行更新。

6、怎么在 Vue 中定义全局方法?

在 Vue.js 中定义全局方法,可以通过多种方式实现,包括直接在 Vue 的原型对象上添加方法、使用 Vue 3 的全局 API (app.config.globalProperties)、以及通过混入 (mixin) 等方法。

方法一:在 Vue 2 中通过 Vue.prototype 定义全局方法

方法二:在 Vue 3 中通过 app.config.globalProperties 定义全局方法

方法三:使用混入(Mixin)

7、什么是Vite?

基于esbuild与Rollup,依靠浏览器自身ESM编译功能, 实现极致开发体验的新一代构建工具!

Webpack通过先将整个应用打包,再将打包后代码提供给dev server,开发者才能开始开发。

Vite直接将源码交给浏览器,实现dev server秒开,浏览器显示页面需要相关模块时,再向dev server发起请求,服务器简单处理后,将该模块返回给浏览器,实现真正意义的按需加载。

8、Vue2.0为什么不能检查数组的变化,该怎么解决?

  • 无法检测数组/对象的新增
  • 无法检测通过索引改变数组的操作。

Vue检测数据的变动是通过Object.defineProperty实现的,所以无法监听数组的添加操作是可以理解的,因为是在构造函数中就已经为所有属性做了这个检测绑定操作。

  • 无法检测通过索引改变数组的操作。即vm.items[indexOfItem] = newValue?

官方文档中对于这两点都是简要的概括为“由于JavaScript的限制”无法实现,而Object.defineProperty是实现检测数据改变的方案,这个限制是指Object.defineProperty

思考

vm.items[indexOfItem] = newValue真的不能被监听么?

Vue对数组的7个变异方法(push、pop、shift、unshift、splice、sort、reverse)实现了响应式。这里就不做测试了。我们测试一下通过索引改变数组的操作,能不能被监听到。

遍历数组,用Object.defineProperty对每一项进行监测

数组

this.$set(array, index, data)

对象

  1. this.$set(obj, key ,value) - 可实现增、改
  2. watch时添加deep:true深度监听,只能监听到属性值的变化,新增、删除属性无法监听

9、Vue 3.0中Treeshaking特性是什么,并举例进行说明?

Tree shaking是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫Dead code elimination

10、Vue3.0 性能提升主要是通过哪几方面体现的?

可以看到,组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多 diff 和遍历其实都是不需要的,造成性能浪费

因此,Vue3在编译阶段,做了进一步优化。主要有如下:

  • diff算法优化
  • 静态提升
  • 事件监听缓存
  • SSR优化

diff算法优化

vue3diff算法中相比vue2增加了静态标记

关于这个静态标记,其作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较

下图这里,已经标记静态节点的p标签在diff过程中则不会比较,把性能进一步提高

静态提升

Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用

这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用

事件监听缓存

默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化

SSR优化

当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染

源码体积

相比Vue2Vue3整体体积变小了,除了移出一些不常用的API,再重要的是Tree shanking

任何一个函数,如refreavtivedcomputed等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小

响应式系统

vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加gettersetter,实现响应式

vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历

  • 可以监听动态属性的添加
  • 可以监听到数组的索引和数组length属性
  • 可以监听删除属性

11、Vue3.0的设计目标是什么?做了哪些优化?

Released under the MIT License.