刷刷前端面试题

606 阅读5分钟

vue2和vue3的区别

一. 根节点不同

vue2中必须要有根标签。

vue3中可以没有根标签,会默认将多个根标签包裹在一个fragement虚拟标签中,有利于减少内存。

二. 组合式API和选项式API

在vue2中采用选项式API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。

在vue3中采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。

三. 生命周期的变化

  • 创建前:beforeCreate -> 使用setup()
  • 创建后:created -> 使用setup()
  • 挂载前:beforeMount -> onBeforeMount
  • 挂载后:mounted -> onMounted
  • 更新前:beforeUpdate -> onBeforeUpdate
  • 更新后:updated -> onUpdated
  • 销毁前:beforeDestroy -> onBeforeUnmount
  • 销毁后:destroyed -> onUnmounted
  • 异常捕获:errorCaptured -> onErrorCaptured
  • 被激活:onActivated 被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
  • 切换:onDeactivated 比如从 A 组件,切换到 B 组件,A 组件消失时执行

四.v-if和v-for的优先级

在vue2中v-for的优先级高于v-if,可以放在一起使用,但是不建议这么做,会带来性能上的浪费

在vue3中v-if的优先级高于v-for,一起使用会报错。可以通过在外部添加一个标签,将v-for移到外层

五.diff算法不同

vue2中的diff算法

遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方。用patch记录的消息去更新dom

缺点:比较每一个节点,而对于一些不参与更新的元素,进行比较是有点消耗性能的。特点:特别要提一下Vue的patch是即时的,并不是打包所有修改最后一起操作DOM,也就是在vue中边记录变更新。(React则是将更新放入队列后集中处理)。

vue3中的diff算法

在初始化的时候会给每一个虚拟节点添加一个patchFlags,是一种优化的标识。只会比较patchFlags发生变化的节点,进行识图更新。而对于patchFlags没有变化的元素作静态标记,在渲染的时候直接复用。

六. 响应式原理不同

vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现,Object.definedProperty()会遍历每一个属性。

vue3通过proxy代理的方式实现。

proxy的优势:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。

当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升

vue3中ref和reactive的区别

  1. 📌 ref与reactive的主要区别在于它们对数据类型的处理方式。ref为基本类型数据添加一层响应式包装,而reactive则将引用类型数据(如对象或数组)转换为响应式对象。

  2. 📌 使用ref包装后,需要通过.value来访问和修改数据。这可能导致在定义对象属性时避免使用与value相关的名称,以免混淆。

  3. 📌 ref主要用于定义基本类型数据,而reactive则用于定义引用类型数据,如对象或数组。值得注意的是,ref内部会自动通过reactive转换为代理对象。

  4. 📌 在使用ref定义的数据时,操作数据需要使用.value,但在模板中直接读取数据时不需要.value。而使用reactive定义的数据时,操作和读取数据都不需要.value。

  5. 📌 当使用reactive创建的响应式对象作为属性值时,该属性仍然是响应式的。而使用ref创建的引用作为属性值时,该属性不会自动成为响应式。如果需要将嵌套引用转换为响应式,需要使用toRef或toRefs。

watch与watch effect的区别

watch 是一个选项API,在组件的选项中使用,可监听指定的数据变化,并执行回调函数。

  • 它需要显式指定要监听的数据和回调函数。
  • 当被监听的数据变化时,Vue会自动调用回调函数。
  • 相比之下,watch提供了更多的控制选项,例如可以设置深度监听、立即执行回调函数、手动停止监听等。
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ double }}</p>
  </div>
</template>
 
<script>
import { reactive, watch, watchEffect } from 'vue';
 
export default {
  setup() {
    const state = reactive({
      count: 0,
    });
 
    // 使用 watch 监听 count 的变化
    watch(
      () => state.count,
      (newVal, oldVal) => {
        console.log(`count 变化,新值为 ${newVal},旧值为 ${oldVal}`);
      }
    );
 
    // 使用 watchEffect 响应式地追踪 count
    watchEffect(() => {
      state.double = state.count * 2;
    });
 
    return {
      count: state.count,
      double: state.double,
    };
  },
};
</script>

watchEffect是一个函数API,在组件的setup函数或生命周期函数中使用。它会自动追踪依赖的响应式数据,并在数据变化时执行回调函数。

  • 与watch不同,watchEffect不需要显式地指定要监听的数据,它会自动追踪函数内部使用的响应式数据。
  • 由于watchEffect的自动追踪机制,它通常用于处理副作用,例如执行异步操作或者更新UI

reactive失去响应性的情况,以及如何解决

失去响应式的情况如下:

第一种:对指定的变量进行重新赋值

第二种:第二种是结构赋值

<template>
 <h1>{{ foo.a }}</h1>
 <h1>{{ bar.a }}</h1>
 <button @click="handleClick">点我</button>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'

let foo = ref({ a: 1, b: 2, c: 3 })
let bar = reactive({ a: 4, b: 5, c: 6 })

const handleClick = () => {
 foo.value = {
   a: 11
 }
 bar = {
   a: 99
 }
 console.log('handleClick-->foo', foo)
 console.log('handleClick-->bar', bar)
}
onMounted(() => {
 console.log('onMounted-->foo', foo)
 console.log('onMounted-->bar', bar)
})
</script>

解决失去响应性的两种方式:

1.多层嵌套,在用reactive时,加一层嵌套对象,在变换取值时以对象的方式进行替换。

const num3 = reactive({

na: {

a: 1,

b: 2,

},

});

  


const replace = { a: 2, b: 4 };

  


num3.na = replace;

2.将ref取代reactive,书写上麻烦点,但是不会失去响应性