Bootstrap

2024总结的vue3的面试题

一、vue2和vue3的区别

答案:

1、数据绑定原理不同

vue2:vue2的数据绑定是利用ES5的一个API:Object.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。

vue3:vue3中使用了ES6的Proxy API对数据代理。相比vue2.x,使用proxy的优势如下:

defineProperty只能监听某个属性,不能对全对象监听

可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)

可以监听数组,不用再去单独的对数组做特异性操作vue3.x可以检测到数组内部数据的变化。

2、是否支持碎片

vue2:vue2不支持碎片。

vue3:vue3支持碎片(Fragments),就是说可以拥有多个根节点。

3、API类型不同

vue2:vue2使用选项类型api,选项型api在代码里分割了不同的属性:data,computed,methods等。

vue3:vue3使用组合式api,新的合成型api能让我们使用方法来分割,相比于旧的api使用属性来分组,这样代码会更加简便和整洁。

4、定义数据变量和方法不同

vue2:vue2是把数据放入data中,在vue2中定义数据变量是data(){},创建的方法要在methods:{}中。

vue3:,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。使用以下三个步骤来建立反应性数据:

从vue引入reactive;

使用reactive() 方法来声明数据为响应性数据;

使用setup()方法来返回我们的响应性数据,从而template可以获取这些响应性数据。

5、生命周期钩子函数不同

vue2:vue2中的生命周期:

beforeCreate 组件创建之前
​
created 组件创建之后
​
beforeMount 组价挂载到页面之前执行
​
mounted 组件挂载到页面之后执行
​
beforeUpdate 组件更新之前
​
updated 组件更新之后

vue3:vue3中的生命周期:

setup 开始创建组件前
​
onBeforeMount 组价挂载到页面之前执行
​
onMounted 组件挂载到页面之后执行
​
onBeforeUpdate 组件更新之前
​
onUpdated 组件更新之后

而且vue3.x 生命周期在调用前需要先进行引入。除了这些钩子函数外,vue3.x还增加了onRenderTracked 和onRenderTriggered函数。

6、父子传参不同

vue2:父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象。

vue3:父传子,用props,子传父用事件 Emitting Events。在vue3中的setup()中的第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。

7、指令与插槽不同

vue2:vue2中使用slot可以直接使用slot;v-for与v-if在vue2中优先级高的是v-for指令,而且不建议一起使用。

vue3:vue3中必须使用v-slot的形式;vue3中v-for与v-if,只会把当前v-if当做v-for中的一个判断语句,不会相互冲突;vue3中移除keyCode作为v-on的修饰符,当然也不支持config.keyCodes;vue3中移除v-on.native修饰符;vue3中移除过滤器filter。

8、main.js文件不同

vue2:vue2中我们可以使用pototype(原型)的形式去进行操作,引入的是构造函数。

vue3:vue3中需要使用结构的形式进行操作,引入的是工厂函数;vue3中app组件中可以没有根标签。

9、Vue3带来了什么

更快的渲染速度:Vue3使用了Proxy代理对象,可以更快地跟踪数据变化,从而提高渲染速度。

更小的体积:Vue3的体积比Vue2更小,同时也支持按需加载,减少了页面加载时间。

更好的TypeScript支持:Vue3对TypeScript的支持更加完善,可以更好地进行类型检查和代码提示。

更好的组件封装:Vue3引入了Composition API,可以更好地封装组件逻辑,使得组件更加可复用和易维护。

更好的响应式系统:Vue3的响应式系统进行了重构,可以更好地处理嵌套对象和数组的变化,同时也提供了更多的API来处理响应式数据。

总之,Vue3带来了更好的性能、更小的体积、更好的TypeScript支持、更好的组件封装和更好的响应式系统,使得开发者可以更加高效地开发Vue应用。

二、vue2和vue3响应式原理(Object.defineProperty和Proxy的区别)

一、 vue2的响应式原理
​
1、vue2的响应式实现主要是利用了Object.defineProperty的方法里面的setter 与getter方法,来完成数据劫持
​
2、使用观察者模式来实现响应式的变化。
​
3、具体做法:
     在组件初始化时会给data的每一个属性注册getter和setter,然后再new 一个自己的Watcher对象,此时watcher会立即调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,将当前的Watcher函数注册进sub里(订阅)。当data属性发生改变之后,就会遍历sub里所有的watcher对象,通知它们去重新渲染组件(发布)。
3、Vue2的响应式原理存在一些缺陷,例如无法监听数组的变化,需要通过特殊的方法来实现
​
​
二、vue3的响应式原理:
​
vue3用Proxy代替了Object.defineProperty。
​
1、Proxy 可以直接监听对象而非属性,可以直接监听数组的变化;
2、Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的。
3、Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
4、Proxy也解决了数组监听的问题,不用再去单独的对数组做特异性操作vue3.x可以检测到数组内部数据的变化。也解决了用下标修改数组元素不是响应式的问题。
5、Proxy的性能比Object.definePropery高10倍。
6、Vue3使用了WeakMap来存储依赖关系,避免了Vue2中Watcher的内存泄漏问题。
​

三、组合式api和选项式api的区别

1、组织代码的方式:

1)、选项式api是按照选项(配置项)的思路来分隔组织代码的,例如 data、methods 和 mounted。这些选项所定义的属性都会暴露在函数内部的 this 上,指向当前的组件实例。

2)、组合式api是按照业务逻辑的方式分隔组织代码的。组合式api中使用函数的方式代替选项式api的配置项。让代码组织更加灵活,在任何需要的地方调用对应的函数。比如:在需要定义响应式数据的地方,直接调用ref或者reactive函数既可以(不需要像选项式api那样,非得写在data配置项里)。

2、代码的复用:

1)、选项式api使用mixin来完成,mixin里也是配置项。容易发生命名冲突且关系不清。不够灵活(如果:只需要引入某个函数,就需要把整个混入对象都引入,颗粒度不够小)。

2)、组合式api使用组合式函数的方式,让代码更加灵活,复用性更好,组件里面的逻辑可以进行模块化。也不存在this的问题。

3、对TS的支持:

组合式api对TS的支持更好。

4、单元测试:

组合式api由于使用的是函数的方式,所以,更方便做单元测试。

4、使用场景:

1)、选项式api适用于小型和中型项目。否则,代码逻辑复杂后,导致组件关系复杂,单个组件内部的代码太多。

2)、组合式api适用于大型项目,当然小中项目也是没有问题。

四、vue3新增的响应式相关的函数

答案:

ref,reactive,readonly,computed,watch,watchEffect

1、ref的理解:

答案:

1)、功能:接受一个内部值,返回一个响应式的、可更改的 ref 对象,ref对象只有一个属性:value。

2)、使用ref对象:模板上不需要写 .value 属性(会自动解构),在js中,使用 .value 来完成数据的读写。

3)、ref接收基本类型和引用类型

  • ref可以接收基本类型。

  • ref也可以接收引用类型:如果将一个对象传给 ref函数,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。

  • ref还可以获取组件对象。

4)、原理:

对于传入的基本数据类型----采用objct.defineproperty给value属性做一个getter和setter

引用类型----采用proxy进行包装代理

2 、reactive的理解:

答案:

1)、功能: 接受一个对象,返回一个对象的响应式代理(proxy)。返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

2)、注意点:当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

五、ref和reactive的区别:

相同点:

都是定义响应式数据的

不同点:

1)、可接受的数据类型

ref:可以接受基本类型和引用类型

reactive:只能接受引用类型

2)、使用

ref:模板上不需要写 .value 属性(会自动解构),在js中,使用 .value 来完成数据的读写。

reactive:是Proxy对象,所以,直接使用

3)、原理:

ref:针对基本类型会使用Object.defineProperty给value属性做一个getter和setter,针对引用类型,会使用Proxy。

reactive:背后使用reactive。

六、补充:对比选项式和组合式的对应:

1、定义响应式数据:

选项式在data配置项中,组合式用函数:ref,reactive

2、计算属性:

选项式在computed配置项中,组合式用函数:computed

3、监听(侦听)

选项式在watch配置项中,组合式用函数:watch和watchEffect

4、方法(函数):

选项式在methods配置项中,组合式就使用原生的方式定义函数就行

5、路由: 选项式在main.js中使用Vue.use(router对象),然后在组件里通过 this.$router和this.$route

组合式中使用钩子函数:useRouter()和useRoute()获取

6、vuex

选项式在main.js中使用Vue.use(仓库对象),然后在组件里通过 this.$store

组合式中使用钩子函数:useStore()获取

7、总结:

选项式中的一切,在组合式api中,都用函数完成。

七、toRef和toRefs

1、相同点: ​ toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用,也就是你从 reactive 复制过来的属性进行修改后,除了视图会更新,原有 ractive 里面对应的值也会跟着更新,如果你知道 浅拷贝 的话那么这个引用就很好理解了,它复制的其实就是引用 + 响应式 。

2、不同点:

toRef: 复制 reactive 里的单个属性并转成 ref

toRefs: 复制 reactive 里的所有属性并转成 ref

八、shallowReactive 与 shallowRef

1、shallowRef:只处理基本数据类型的响应式

2、shallowReactive:只处理对象最外层属性的响应式(浅响应式)

3、浅层作用的响应式数据处理:只处理第一层对象的数据,再往下嵌套的数据,操作数据是不起作用的

4、shallowReative与shallowRef在某些特殊的应用场景下,是可以提升性能的,前者针对对象,用于浅层作用的响应式数据处理,而后者只处理基本数据类型的响应式,不进行对象的响应式处理。

九、readonly 与 shallowReadonly【扩展】

readonly与shallowReadonly都是让响应式数据只具备读的能力,后者是浅层次的只读,也就是只对数据对象第一层起作用,深层次的嵌套,当时用shallowReadonl()处理时,深层次数据支持被修改

1、readonly: 让一个响应式数据变为只读的 (深只读),让一个响应式数据变为只读的,接收一个响应式数据,经过readonly加工处理一下,那么新赋值的数据都不允许修改

2、接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理

3、shallowReadonly: 让一个响应式数据变为只读的 (浅只读),接收一个响应式数据,经过shallowreadonly的处理,变成一个只读的,只考虑对象的第一层数据,不可以修改,但是第一层嵌套里的深层数据却支持修改

4、让一个响应式数据变为只读能力(浅只读)

十、toRaw与markRaw转换为普通数据和标记属性非响应式【扩展】

toRaw,将响应式对象(由 reactive定义的响应式)转换为普通对象,然后赋值给新的变量(不影响原来的对象)

markRaw,标记一个对象,使其不能成为一个响应式对象。

toRaw: 作用: 将一个由 reactive 生成的响应式对象转为普通对象 使用场景:

1、用于读取响应式对象对应的普通对象

2、对这个普通对象的所有操作,不会引起页面更新。

markRaw: 作用:标记一个对象,使其永远不会再成为响应式对象 应用场景: 1、有些值不应被设置为响应式的,例如复杂的第三方类库等 2、当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

十一、自定义hooks(组合式函数)的规则和作用:

1、函数名以小写的use开头

2、hooks里可以使用vue组件的相关特性(如:生命周期钩子函数,响应式数据等等)

3、hooks调用:

1)、hooks必须在setup函数的根级调用,即:在setup函数执行时,hooks就要被调用

2)、hooks函数可以在其它hooks函数里调用。

4、可以补充:

1)、参数尽量兼容响应式数据和普通数据

2)、返回值最好时由多个响应式数据组成的普通对象(json对象)

5、作用:

以函数形式抽离一些可复用的方法像钩子一样挂着,放在外部的js文件里。随时可以引入和调用,实现高内聚低耦合的目标;引用时将响应式变量或者方法显式解构暴露出来如:const {nameRef,Fn} = useXX()

十二、Vue3自定义Hooks和Vue2时代Mixin的关系:

1)、Mixin 很容易发生冲突:因为每个 mixin 的 property 都被合并到同一个组件中,所以为了避免 property 名冲突,你仍然需要了解其他每个特性。

2)、mixin的可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。

3)、Mixin同名变量会被覆盖,Vue3自定义Hook可以在引入的时候对同名变量重命名

十三、setup配置(setup函数的参数)

Vue 3中的 setup 函数接收两个参数,分别是 props 和 context。

1、props:父组件传来的属性值

包含: 组件外部传递过来。切组件内部声明接收了的属性。需要注意的是,Vue3中的 props 是只读的,即在setup 函数中不能修改 props 的值。如果需要修改传递过来的数据,可以使用响应式对象或ref。

2、context:上下文对象。

1)、attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性。相当于this.$attrs

2)、slots:收到的插槽内容,相当于this.$slots

3)、emit:分发自定义事件的函数,相当于this.$emit

注意:

1、这个钩子会在created之前执行

2、内部不存在this

3、如果返回值是一个对象,那么这个对象中的键值对会被合并到created钩子的this中,而在视图上也能访问相应的数据值

十四、watch与watchEffect

相同点:

watch和watchEffect都是vue3中的监听器,但是在写法和使用上是有区别的,

不同点:

1)、监听源:

watch函数的监听源会明确指定,是watch函数的第一个参数。

watchEffect函数的监听源是回调函数中使用的所有的响应式数据。自动收集依赖。

2)、懒侦听:

watch默认是懒侦听,如果要立即执行,则设置第三个参数的配置项immediate 为true。

watchEffect不是懒侦听,组件一旦执行,那么,回调函数立即会调用

3)、深度侦听:

watch默认不是深度侦听,如果需要,则设置第三个参数的配置项deep为true。

watchEffect默认是深度侦听的。

4)、是否能够拿到旧值:

watch可以。

watchEffect不可以。

十五、provide与inject

1、provide和inject是一对新的API,用于在父组件中提供数据,然后在子组件中注入数据。

2、provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。如果希望提供的数据是响应式的,那么,值就需要是响应式的数据。

3、inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。

//在父组件中,使用provide提供数据:
//name:定义提供 property的 name。
//value :property的值。
 setup(){
    provide('info',"值")
  }
​
//在子组件中,使用inject注入数据
//name:接收 provide提供的属性名。
//default:设置默认值,可以不写,是可选参数。
setup(){
    const info = inject("info")
    inject('info',"设置默认值")
    return {
        info
    }
  }

十六、vue3新的生命周期钩子

1、Vue3中新增了三个生命周期钩子函数:

onRenderTracked:在组件初次渲染时,可以跟踪到是哪个属性触发了更新。函数的参数(是个对象)里的key就是依赖的属性

onRenderTriggered:在组件更新时,可以跟踪到是哪个属性触发了更新。函数的参数(是个对象)里的key就是依赖的属性

setup函数,调用时机比beforeCreate还早。

2、修改:

vue2中的beforeDetory用beforeUnmount替代

vue2中的detoryed用unmounted替代

3、在组合式api中。

1)、没有提供beforeCreate和created对应的函数,用setup代替

2)、其它的生命周期钩子函数,都是在选项式的单词前面加上了on。

十七、响应式数据的判断isRef、isReactive、isReadonly、isProxy

1、isRef:判断一个值是否为一个 ref 对象

2、isReactive:判断一个对象是否是由 reactive创建的响应式代理

3、isReadonly:判断一个对象是否是由 readonly 创建的只读代理

4、isProxy:判断一个对象是否是由 reactive 或 readonly 创建的代理

十八、vue3中使用插槽?

1、作用:插槽 slot 通常用于父子组件之间,父组件可以改变子组件内部的部分模板,给布局带来了很大的灵活性。

2、使用:

1)、slot 是 Vue 中的内置标签。

2)、在子组件的模板里使用<slot></slot>的方式制定插槽的位置,也就是父组件传递(分发)来的标签所在的位置。在父组件里只需要把插入插槽的标签放在一对子组件标签里(相当于html中,一个标签的内容)。

3、具名插槽:具有名字的 插槽。

定义插槽:

如果在子组件里需要提供多个插槽,就需要给插槽起名字。这就是具名插槽。在组件里写上如此格式的代码:<slot name="xx"> 。

2)、给插槽分发内容。

在父组件里,建议使用<template>标签包裹插槽的内容,代码有几种写法:

2.1)、官方标签上使用slot属性

<div slot="插槽名">我是div1</div>

2.2)、内容比较多时,可以使用<template>包起来。

<template>里,可以使用slot属性,也可以使用v-slot:,v-slot:可以简写为#

2.2.1)、slot属性:

<template slot="插槽名">
    <div></div>
    <div></div>
</template>
​

2.2.2)、 v-slot:插槽名

<template v-slot:插槽名>
    <div></div>
    <div></div>
</template>

2.2.3)、 v-slot 可以缩写为:#

<template #插槽名>
   <h1>Here might be a page title</h1>
</template>

4 、作用域插槽:

插槽里的内容(在父组件的模板上)的数据希望来自于子组件(即就是插槽的内容的作用域变成子组件)。

1)、子组件里定义插槽(的出口),并传递数据。

<slot name="插槽名" 属性名1="属性值1" 属性名2="属性值2"></slot>

2)、父组件中给插槽分发内容,插槽里使用子组件传递的数据。

声明或者定义一个对象作为插槽名的值#插槽名=对象名。该对象就是slot标签里所有属性组成的键值对。

<子组件标签>
    <template  #插槽名=对象名>
        <div>{{对象名.属性值1}}</div>
        <div>{{对象名.属性值2}}</div>
    </template>
</子组件标签>

十九、vue3中使用vue-router,useRoute和useRouter ?

1、在Vue.js中,useRoute和useRouter是Vue Router提供的两个钩子函数,用于在组件中访问路由信息和路由实例。

2、useRoute()的返回值相当于是vue2中的this.$route

import { useRoute } from 'vue-router'
export default {
    setup() {
         const route = useRoute()
         console.log(route.path) // 当前路由路径
         return {
           route
         }
    }
}
​

3、useRouter()的返回值相当于是vue2中的this.$router,可以使用useRouter进行路由跳转

import { useRouter } from 'vue-router'
export default {
    setup() {
         const router = useRouter()
         console.log(router.currentRoute.value.path) // 当前路由路径
      return {
        router
      }
   }
}

二十、vue3中nextTick使用

vue3组合式api中,使用 nextTick()函数,是独立的函数,不需要像选项式api那样,通过this.$nextTick()。作用和vue2中一样。

二十一、vue3中定义全局属性

1、通过config.globalProperties

2、通过provide注入:在应用实例上设置一个可以被注入到应用范围内所有组件中的值。当组件要使用应用提供的 provide 值时,必须用 inject 来接收。

3、在main.js中全局引入,然后在组件中获取

;