一、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中全局引入,然后在组件中获取