Vue3(不断记录中...)

109 阅读9分钟

ES6的Proxy的理解

Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

vue3中proxy的理解

Vue3中,响应式数据的实现方式从原来Vue2Object.defineProperty()更换为了Proxy,
本质上还是对数据进行拦截,通过getter和setter实现页面的响应式更新。
除了常用的 get/set 外,Proxy 可以定义13种拦截操作。并且 Proxy 还可以进行取消操作,
可以通过 Proxy.revocable() 返回一个可取消的实例。
Proxy.revocable()的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,
就收回代理权,不允许再次访问。
而针对如此万能的 Proxy 需要注意的问题是当一个对象被劫持后该对象的 this 指向将变为 Proxy 。

在vue2中Object.definePerty()存在的问题:
Object.defineProperty 的缺陷在于需要深度遍历并对每一个属性进行劫持,而对于没有属性的数组而言,
数组的索引也可以视为被劫持的属性,但是和对象相同,对于新增的元素而言,不会触发监听事件,
vue 对此的解决方案是劫持数组原型链上的函数,即便如此也仍旧无法监听对数组长度的修改。

proxy数据类型转换

proxy类型转成普通类型:JSON.parse(JSON.stringify(this.newlist));

setup

vue3中一个新的配置项,值为一个函数,该函数相当于一个生命周期函数
setup 函数会在 beforeCreate 之后、created 之前执行,vue3 也是取消了这两个钩子
组件中所要用到的数据、方法等,都要配置到setup中
两种返回值:
a、若返回一个对象,则对象中的属性、方法都可以在模板中直接使用
b、若返回一个渲染函数,则可以自定义渲染内容
两个注意点:
a、setup执行时机,在beforeCreate之前执行一次,this是undefined
b、setup的参数:
props:值为对象,包含组件外部传递过来,且组件内部声明接收了的属性
context:上下文对象
attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs;
slots:收到的插槽内容,相当于this.$slots;
emit:分发自定义事件的函数,相当于this.$emit。

setup的执行时组件对象还没有创建,此时不能使用this来访问data/computed/methods/props
我们可以通过 getCurrentInstance这个函数来返回当前组件的实例对象,也就是当前vue这个实例对象
import {defineComponent, getCurrentInstance} from 'vue';
export default defineComponent({
  setup () {
    const {proxy} = getCurrentInstance()
    console.log(proxy)
  } 
})
proxy.$attrs
proxy.$data
proxy.$el...

VUE3路由新玩法:userRouter和userRoute

原来的vue2路由是通过this.$route和this.$router来控制的。现在vue3有所变化,
useRoute相当于以前的this.$route,而useRouter相当于this.$router
useRouter(跳转), useRoute(获取路由参数)

defineComponent

在定义 Vue 组件时提供类型推导的辅助函数。
把接收的 Object 直接返回。但是,就类型而言,返回的值有一个合成类型的构造函数,
用于手动渲染函数、TSXIDE 工具支持。
import { defineComponent,ref } from 'vue';
export default defineComponent({
  setup() {
    const num = ref(1)
    return { num }
    }
});

ref函数

作用:定义一个响应式数据
语法:const xxx = ref(initValue)
创建一个包含响应式数据的引用对象(reference对象,简称ref对象);
Js中操作数据:xxx.value;模块中读取数据不需要value,直接{{xxx}}
备注:接收的数据可以是基本类型也可以是对象类型
基本类型的数据响应式依然靠Object.defineProperty()的getset完成的
对象类型的数据:内部求助了vue3中的一个新函数--reactive函数

reactive函数

作用:定义一个对象类型的响应式数据(基本类型用ref)
语法:const 代理对象=reactive(被代理对象)
接收一个对象(或数组),返回一个代理对象(proxy对象)
{{state.name}}
setup(props, context) {
    let state = reactive({
      name: 'test'
    });
    return state
}

reactive和ref对比

从定义数据角度对比
ref用来定义基本数据类型;
reactive用来定义对象(数组)类型数据
备注:ref也可以用来定义对象或数组类型数据,它内部会自动通过reactive转化为代理对象
从原理角度对比:
ref通过Object.defineProperty()的getset来实现响应式
reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
从使用角度对比:
ref定义的数据:操作数据需要value,读取数据时模板中直接读取不需要value
reactive定义的数据:操作数据与读取数据均不需要value

vue3的响应式原理

实现原理:
通过proxy代理:拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除等;
通过Reflect反射:对被代理对象的属性进行操作;

vue3中的计算属性和监听

1、computed函数
首先,先导入: import {computed} from 'vue'
写法:
 person.fullName=computed({
    get(){return person.firstName+'-'+person.lastName},
    set(value){person.firstName=nameArr[0]}
})
2、watch函数
watch函数可以传递三个参数,第一个参数是监视的属性,第二个是执行的回调函数,第三是配置
情况一:监视ref所定义的一个数据
watch(sum,(newValue,oldValue)=>{
    console.log('sum变了',newValue,oldValue);
},{immediate:true})
情况二:监视ref所定义的多个响应数据
watch([sum,msg],(newValue,oldValue)=>{
    console.log('sum变了huomsg变了',newValue,oldValue);
})
情况三:监视reactive所定义的一个响应式数据 的全部属性
注意:1、无法正确的获取oldValue 2、强制开启了深度监视(deep配置无效)
 watch(person,()=>{
    console.log('person变化了');
})
情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.age,(newValue,oldValue)=>{
    console.log('age变化了',newValue,oldValue);
})
情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
    console.log('age,name变化了',newValue,oldValue);
})
注意特殊情况 当监视reactive所定义的对象中的某个属性,这个时候deep配置有效

watchEffect函数

watch:既要指明监视的属性,又要指明监视的回调
watchEffect:不用指明监视哪个属性,监视的回调用到哪个属性就监视哪个属性
watchEffect有点像computed,
但是computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
而watchEffect更注重过程(回调函数的函数体),所以不用写返回值
watchEffect(()=>{
    const x1=sum.value
    const x2=person.job.j1.salary
    console.log('watchEffect所指定的回调函数执行了',x1,x2);
})

vue3中的生命周期

在vue3中,新增了一个setup生命周期函数,setup执行的时机是在beforeCreate生命函数之前执行,
因此在这个函数中是不能通过this来获取实例的;同时为了命名的统一,beforeDestroy
改名为beforeUnmount,destroyed改名为unmounted

vue3中可以继续使用vue2中的生命周期钩子,但是有两个被更名
beforeDestroy改名为beforeUnmount;
destroyed改名为unmounted
vue3中也提供了compositionAPI形式的生命周期钩子,与vue2中钩子的对应关系如下:
beforeCreate => setup()
created => setup()
意思是setup相当于beforeCreate和created,所以不用往setup中放;
beforeMount=>onBeforeMount
mounted=>onMounted
beforeUpdate=>onBeforeUpdate
updated=>onUpdated

vue3中的自定义hook函数

定义:本质上是一个函数,把setup函数中使用的compositionAPI进行了封装
类似于vue2的mixin
自定义hook的优势,复用代码,让setup中的逻辑更清楚易懂

vue3中的toRef

作用:创建一个ref对象,其中value值指向另一个对象中的属性
语法:name: toRef(person, 'name')
应用:要将响应式对象的某个属性单独提供给外部使用时;
扩展:toRefs和toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)

shallowReactive和shallowRef

shallowReactive只处理对象最外层的响应式(浅响应式)
shallowRef只处理基本数据类型的响应式,不进行对象式响应式的处理
如果有一个对象数据,结构比较深,但变化时只是外层属性变化,用shallowReactive
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象替换,用shallowRef

readOnly和shallowReadOnly

readOnly:让一个响应式数据变成只读的(深只读)
shallowReadOnly:让一个响应式数据变成只读(浅只读)
应用场景:不希望数据被修改时;shallowReadOnly(person)

toRaw和markRaw

toRaw:
作用:将一个由reactive生成的响应式对象转换为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起对象更新
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象
应用场景:
a、有些值不应该被设置为响应式的,例如复杂的第三方类库
b、当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

customRef

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制

provide和inject

实现祖与后代组件的通信

响应式数据的判断

isRef:检查一个值是否为一个ref对象;
isReactive:检查一个对象是否由reactive创建的响应式代理;
isReadonly:检查一个对象是否由readonly创建的响应式代理;
isProx:检查一个对象是否由reactive或readonly创建的响应式代理;

Fragment

在vue2中,组件必须有一个根标签;
在vue3中,组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中;
好处是减少标签层级,减小内存占用。

Teleport

Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
<teleport to='移动位置' ></teleport>

suspense

等待异步组件时渲染一些额外的内容,让应用有更好的用户体验
使用步骤:
异步引入组件;
使用suspense包裹组件,并配置好default和fallback