当前位置: 首页 > 科技观察

一网打尽──Vue3Composition-api新特性

时间:2023-03-12 18:27:02 科技观察

写得比较早在体验Vue3之前,我们先了解下Vue3有哪些亮点。总共有6大特性:Performance(性能比vue2的runtime快2倍左右)Treeshakingsupport(按需打包模块)BetterTypeScriptsupport(更好的TS代码支持)CompositionAPI(组合API)CustomRendererAPI(customRenderingAPI)Fragment,Teleport,Suspense1.vue3setup1的由来。理解:vue3.0的组合api阶段是setup2。设置的所有数据方法都在设置中配置。3.Setup有两个返回值:一个是返回Object,在模板中可以直接使用对象中的属性和方法。如果返回渲染函数,可以自定义渲染内容。4.注意:vue3.0中的vue2配置尽量不要混用(data、methods、computed等)可以访问vue3的setup属性方法。setup中vue2的配置(data、methods、computed等)是访问不到的。如果名称相同,则以vue3为准。对象,而是一个promise,模板看不到返回对象中的属性(promise后面也可以通过suspense和异步组件获取)2.ref函数的作用:用于定义一个响应式数据语法:constname=ref(initValue)创建一个包含响应式数据的引用对象(referenceobject,简称refobject)。js中操作数据:name.value模板中读取数据:不需要value,直接

{{name}}
注意:接受的数据:基本类型,对象类型基本数据类型:Reactive设置和由Object.defineProperty()的get和set赋值。对象类型数据:内部使用了vue3中的新函数--reactive函数(其实就是proxy代理)3.Reactive函数function:用来定义一个对象类型的响应式数据(基本类型不要使用,使用ref函数)语法:const代理对象(proxy)=reactive(sourceobject)接收一个对象或数组,返回一个代理对象(proxyinstanceobject,简称代理对象)reactive定义的响应性是“深”的,对象中的所有对象内部都基于响应性es6的代理实现通过代理对象的内部数据进行操作。Reactive处理的对象不能使用解构语法,因为它们会失去响应能力。可以使用toRefs将reactive的各个属性转化为ref响应式,这样就可以进行响应式处理ref和reactive的区别reactive是用来将对象数据转化为responsive的,类似于vue2的observable,ref是用来获取个别或基本数据类型转化为响应式。为什么vue3中有两个将数据转换为响应式数据的api,下面详细解释一下:ref接受一个内部值,返回一个响应式可变的ref对象,它有一个指向内部值的属性。valuereactive接受一个对象并返回该对象的响应式副本,它将对象中的每个元素转换为ref对象,它们都转换为ref响应式数据;而ref不仅可以将对象数据转换为响应式数据,还可以处理基本数据类型(string、boolean等)。造成这种差异的原因是vue3中的响应式是基于代理实现的,而代理的目标必须是引用数据类型,即存储在堆内存中并通过指针引用的对象。为什么是代理的引用数据类型?那是因为简单数据类型的每次赋值都是全新的数据,根本无法进行代理,所以很难实现简单数据类型的响应性。如果我们想获得简单数据类型的响应性,应该怎么做呢?vue3中也考虑到了这一点,可以通过ref进行简单数据类型的响应式处理。ref通过创建一个内部状态将值附加到value上,所以ref生成的对象需要通过.value来获取和使用。在重写get/set获取的监听的同时,对象的处理也依赖于reactive的实现。就像这样,ref既可以处理简单的数据类型,也可以处理引用数据类型的响应式处理。在实践中,我们应该避免将reactive顶部的所有变量都声明为vue2的数据,而应该结合具体的应用和逻辑就近声明。classRefImpl{private_value:Tpublicreadonly__v_isRef=trueconstructor(private_rawValue:T,publicreadonly_shallow=false){this._value=_shallow?_rawValue:convert(_rawValue)}getvalue(){track(toRaw(this),TrackOpTypes.GET,'value')returnthis._value}setvalue(newVal){if(hasChanged(toRaw(newVal),this._rawValue)){this._rawValue=newValthis._value=this._shallow?newVal:convert(newVal)触发(toRaw(this),TriggerOpTypes.SET,'value',newVal)}}}...constconvert=(val:T):T=>isObject(val)?reactive(val):val...四、Vue3中响应式principleVue2的响应式原理:对象类型:拦截通过Object.defineProperty()读取和修改属性(数据劫持)数组类型:通过重写更新数组的一系列方法进行拦截。(包裹数组的change方法)Object.defineProperty(data,"count",{get(){},set(){}})有个问题:添加属性,删除属性,界面不会更新同步直接通过下表修改数组,接口不会自动更新vue3代理通过代理的响应原理:拦截对象中任意属性的变化,包括:属性值的读写,属性值的增删改查等通过reflect反射:到代理对象五、react和ref的区别定义数据ref:用来定义基本数据类型reactive:用来定义引用数据类型注意:ref也可以用来定义引用数据类型,内部会通过ractive自动转换为代理对象原理ref:通过Object.defineProperty()的set和get属性实现响应式(数据劫持)reactive:实现响应式s(数据劫持)通过Proxy,通过Reflect操作使用对象内部的数据使用ref:ref定义的数据需要通过.value进行操作,模板读取数据时不需要使用.value.Reactive:reactive定义的数据可以不用.value设置和读取。从“vue”导入{计算};setup(){constsum=computed(()=>{returnnum1+num2;})}从“vue”setup(){constperson=reactive({firstName:"wen",lastName:"bo"})//速记letfullName=computed(()=>{returnperson.firstName+"-"+person.lastName;});//完整letfullName=computed(()=>{get(){returnperson.firstName+"-"+person.lastName;},set(value){constnewArr=value.split("-");person.firstName=nameArr[0];person.lastName=nameArr[1];}})}7.watch函数与vue2中的watch配置函数一致注意:监控reactive定义的reactive数据时,无法正确获取oldValue,强制开启深度监控(深度配置失败)。在reactive定义的reactive数据中监控某个属性时,深度配置有效letperson=reactive({name:"wenbo",age:18,job:{job1:{salary:20}}})//案例一:监控ref定义的响应数据watch(sum,(newValue,oldValue)=>{console.log("sumchanged",newValue,oldValue);},{immediate:true})//情况2:监控多个ref-definedresponsivedatawatch([sum,msg],(newValue,oldValue)=>{console.log("sumormsghaschanged",newValue,oldValue);})/*Case3:监控由定义的反应数据reactive如果watch监控的是reactive定义的reactive数据,则无法正确获取oldValue如果watch监控的是reactive定义的reactive数据,响应数据,此时强制开启deep监控,deep不生效*/watch(person,(newValue,oldValue)=>{console.log("perosnchanged",newValue,oldValue);},{immediate:true,deep:false})//案例四:监听响应式数据中的某个属性由reactivewatch(()=>pers定义on.age,(newValue,oldValue)=>{console.log("Theperson'sagehaschanged",newValue,oldValue)})//情况五:在reactivewatch([()=>person.name,()=>person.age],(newValue,oldValue)=>{console.log("person'snameoragehaschanged",newValue,oldValue)})//特例-监控属性对象仍然是一个对象。此时deep生效,可以进行深度监控。watch(()=>person.job,(newValue,oldValue)=>{console.log("person'sjobhaschanged",newValue,oldValue)},{deep:true})注:watch有五种情况监视由reactive定义的对象。watch监听ref定义的reactive数据有两种情况:ref定义了基本类型的数据。此时无法监控.value。constnum=ref(0),此时监听num.value相当于直接监听num(0)的值,想想0的变化。ref定义了对象数据类型,ref处理的数据此时是ref实例。监听对象的变化,需要监听对象的.value,因为ref对象的.value是reactive处理的响应式数据代理。8、watchEffect函数watch的例程:需要同时指定被监听的属性和回调watchEffect例程:不需要指定监听哪个属性,回调中用到哪个属性监听,监听哪个属性监视器。watchEffect有点类似于computed:Computed关注的是计算出来的值,也就是回调函数的返回值,所以必须要写返回值。watchEffect关注的是计算过程,也就是回调函数的函数名,所以返回值就不用写了letperson=reactive({name:"wenbo",age:18,job:{job1:{salary:20}}})//只要warEffect指定的回调中使用的数据发生变化,就会直接重新执行回调watchEffect(()=>{constx=person.nameconsty=person.job.job1.salaryconsole.log("watchEffecttriggered")})9.自定义hooks函数hook函数:本质上是一个函数,封装了类似于vue2的setup函数中使用的组合apimixins的优点:重用代码,让setup中的逻辑更清晰并且更容易理解
pageX:{{point.pageX}},pageY:{{point.pageY}}
hooksfileusePoint.jsimport{reactive}from"vue"constandleClickPoint=()=>{//实现鼠标“点击”相关数据letpoint=reactive({pageX:0,pagey:0})//实现鼠标“点击”相关方法constandlePoint=(event)=>{point.pageX=event.pageX;point.pageY=event.pageY;console.log(event.pageX,event.pageY)}//实现鼠标点击的相关周期函数onMounted(()=>{window.addEventListener("click",handlePoint)})onBeforeUnmounted(()=>{window.removeEventListener("click",handlePoint)})}10、toRef函数中ref和reactive处理的对象不能直接解构处理,否则会失去响应性。功能:创建一个ref对象,其值指向另一个对象中的属性语法:constname=toRef(person,"name")应用:在响应式对象中提供一个属性供外部单独使用扩展:toRefs和toRef函数是一样,但是可以批量创建多个ref对象。语法:toRefs(person)十一、VUE3生命周期其他结合api1。shallowReactive和shallowRefshallowReactive:只处理对象最外层属性的响应(浅层响应)shallowRef:只处理基本数据类型的响应,不处理对象的响应。什么时候使用:如果有对象数据,结构比较深,但是发生变化时,只是外层属性发生变化==》shallowReactive如果有对象数据,后续函数不会修改属性在对象中,但生成一个新对象来替换=="shallowRef2.readonly和shallowReadonlyreadonly:让一个响应式数据变成只读数据(deepread-only)shallowReadonly:让一个响应式数据变成只读(shallowread-only)应用场景:当你不希望数据被修改时3.toRaw和markRawtoRaw作用:将reactive生成的一个reactive对象转换为普通对象使用场景:用于读取reactive对象对应的普通对象,所有对这个普通对象的操作不会导致页面更新转换可以提高性能作用:标记一个对象,使其永远不会被称为响应对象应用场景:4.customRef作用:创建自定义ref并显式控制其依赖跟踪和更新触发示例:5、provide和inject的功能:实现祖孙之间的通信。应用场景:父组件有一个provide选项提供数据,子组件有一个inject获取使用数据6.响应式数据判断isRef:检查一个值是否为ref对象isReactive:检查一个值是否为areactiveisReadonly创建的reactiveproxy:检查对象是否由readonly创建的只读代理isProxy:检查对象是否为reactive或readonly创建的代理。在vue3中使用钩子。注意hooks存在异步问题。其实在使用中就能发现。其实hooks本质上就是抽取的函数,灵活性比较高,但是在涉及到异步逻辑的时候考虑的不周全会造成很多问题。hook可以覆盖异步情况,但是HTML必须在setup中执行才能返回有效对象,不能被阻塞。通常异步有两种风格:没有其他外部依赖,只传递渲染的响应变量。这种情况下,可以通过声明并对外暴露response变量,在hook中使用异步修改方式。External有依赖,需要在使用端处理。针对这种情况,可以将Promise对外暴露,获得同步操作的能力。这样做的问题是无法获得,因为设置是在生命周期的beforeCreate和created阶段之间。当然我们可以通过setup的第二个参数context获取一些类似这样的能力,但是操作路由、vuex等的能力是有限的。为此,最新的router@4和vuex@4提供了组合的api。由于vue2的底层限制,无法使用这些hooks。为此,可以通过引用实例获取某些操作能力,或者通过getCurrentInstance获取挂载在组件实例上的对象。组合API的响应式原理虽然是通过Object.defineProperty重写属性,和vue的响应式原理是一样的,但是在具体的实现方式上有区别,所以和vue原生的响应式风格没有沟通在设置中。正因为如此,即使获取到了相应的实例,也无法监控其响应情况,只能在选项配置中使用。vue3中常用组件1.vue2中的Fragment组件:组件必须有根标签vue3中:组件可以没有根标签,多个标签会包裹在Fragment虚拟元素里面好处:减少标签层级,减少内存占用2.Teleport非常依赖可以将组件html结构移动到指定位置的技术语法:main.vue
openmodalmodal.vuethisisamodal关闭
3.Suspense在等待异步组件的同时渲染一些额外的内容,让应用有更好的用户体验。使用步骤:异步导入组件使用suspense包装组件,并配置default和fallback。import{defineAsyncComponent}from"vue"constchild=defineAsyncComponent(()=>import("./components/child.vue"))【编辑推荐】鸿蒙官方战略合作共建——鸿蒙技术社区云容器:你有什么选择?5G消息全面进入发展期什么是域名劫持?如何应对域名劫持自学编程,首先应该选择什么语言?一篇文章读懂网络爬虫发展史