ref-3-之间的区别-Vue-中-和-reactive (ref3030)
最近有好友在面试环节中经常被问到这么一个疑问,vue3中的ref和reactive的区别在哪里,为什么要定义两个API一个api不能成功照应式降级吗??
带着这个不懂,我们接上去启动逐个讨论。
1:剖析
1.1refandreactive怎样用?
![ref](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
置信大家都知道在vue3中我们可以经过一些api去定义照应式数据,比如ref,reactive,shallowRef.
ref基本经常使用
<template><div><span>{{inner.content.text}}</span><span>{{count}}</span></div></template><scriptsetup>constinner=ref({content:{text:"外部内容"}});//你可以经过ref定义复杂数据类型//orconstcount=ref(20);//定义个别数据类型</script>
reactive基本经常使用
<template><div><div>{{wer.subText}}</div><divv-for="iteminlist":key="item.id">{{item.content}}</div></div></template><scriptsetup>constwapper=reactive({subText:inner});constlist=reactive([{id:1,content:"render"},{id:2,content:"render2"}]);</script>
当然你还可以配合computedodwatchEffec经常使用这里我就不再过多引见了。
1.2ref和reactive的区别?
置信大家读到这里可以看出ref既可以定义基本数据类型也可以定义复杂数据类型,而reactive只定义复杂数据类型。
那有人就问了?reactive只能存复杂数据类型吗?
答案很显著不是的reactive也可以存基本数据类型
那他们究竟区别在哪里呢?我想这个时刻从我们开发者的角度上没方法看出实质的区别,无非是定义变量呗,那接上去请随者我一同进入源码的是环球。
1.3源码成功流程?
1.3.1如何找到源码?
先回答第一个疑问,怎样找源码,这个须要你对源码的包十分相熟我们可以经过看package.json文件先找到它打包的入口文件,然后再去依据不同的状况找不同的文件。
1.3.2:找到ref函数的源码文件,看看函数外部做了什么事情?
源码文件:corepackagesreactivitysrcref.ts
外围代码成功
//ref.ts文件93行exportfunctionref(value?:unknown){returncreateRef(value,false)//1:提供ref函数,false能否浅复制}//ref.ts文件第127行//调用ref前往一个创立的方法createRef传入两个值/***@paramrawValueref函数传入的参数*@paramshallow能否浅复制*/functioncreateRef(rawValue:unknown,shallow:boolean){if(isRef(rawValue)){//能否是ref对象假设是则间接前往returnrawValue}returnnewRefImpl(rawValue,shallow)//否则创立ref对象传入rawValueshallow}//ref.ts文件第134行classRefImpl<T>{//创立一个ref的成功类private_value:T//创立私有的_value变量private_rawValue:T//创立私有的_rawValue变量publicdep?:Dep=undefined//能否deppublicreadonly__v_isRef=true//只读的属性能否是refconstructor(value:T,publicreadonly__v_isShallow:boolean){//实例被new时口头constructor保留传入的值this._rawValue=__v_isShallow?value:toRaw(value)//能否浅复制,假设时则间接前往传入的值否则启动失掉其原始对象this._value=__v_isShallow?value:toReactive(value)//能否浅复制是前往原value否则转换成reactive对象}getvalue(){//失掉值的时刻间接将constructor保留的值前往trackRefValue(this)//跟踪ref的valuereturnthis._value//失掉value是前往_value对象}setvalue(newVal){//当设置值的时刻往下看//能否浅复制or值身上能否有__v_isShallow标识or能否是只读的标识__v_isReadonlyconstuseDirectValue=this.__v_isShallow||isShallow(newVal)||isReadonly(newVal);//假设满足则前往新设置的值,假设不是则取出新值的原始对象newVal=useDirectValue?newVal:toRaw(newVal)//假设你一个浅层对象(个别数据类型)则原值前往否则判别能否能从代理对象中取出源值if(hasChanged(newVal,this._rawValue)){//判别对象能否出现变动变了向下走this._rawValue=newVal//将最新值赋给_rawValuethis._value=useDirectValue?newVal:toReactive(newVal)//判别能否是基本数据类型假设是则将最新值前往否则继续转换reactivetriggerRefValue(this,newVal)//触发ref的value值启动监听降级}}}//判别能否是对象假设是则reactive代理否则前往以后的valueexportconsttoReactive=<Textendsunknown>(value:T):T=>isObject(value)?reactive(value):value
以上代码就是ref的外围成功,置信看来如同源码也没有那么难。
1.3.3.总结一下ref做了什么?
1.3.4:找到reactve函数的源码文件,看看函数外部做了什么事情?
reactive.ts
exportfunctionreactive(target:object){//iftryingtoobserveareadonlyproxy,returnthereadonlyversion.if(isReadonly(target)){//假设是只读的不准许写入则前往只读对象returntarget}returncreateReactiveObject(//前往一个创立reactive的对象target,//传入指标对象false,//能否是只读对象mutableHandlers,//提供get,set,deleteProperty,has,ownKeys方法mutableCollectionHandlers,//太多了自己看源码reactiveMap//提供一个weakmap汇合)}functioncreateReactiveObject(target:Target,isReadonly:boolean,baseHandlers:ProxyHandler<any>collectionHandlers:ProxyHandler<any>,proxyMap:WeakMap<Target,any>){if(!isObject(target)){//假设不是一个对象则前往以后tragetif(__DEV__){console.warn(`valuecannotbemadereactive:${String(target)}`)}returntarget}//targetisalreadyaProxy,returnit.//exception:callingreadonly()onareactiveobjectif(target[ReactiveFlags.RAW]&&//假设target曾经是一个代理对象则前往以后对象!(isReadonly&&target[ReactiveFlags.IS_REACTIVE])){returntarget}//targetalreadyhascorrespondingProxyconstexistingProxy=proxyMap.get(target)//假设对象曾经有了代理对象则间接取值前往if(existingProxy){returnexistingProxy}//onlyspecificvaluetypescanbeobserved.consttargetType=getTargetType(target)//观察指定类型if(targetType===TargetType.INVALID){returntarget}constproxy=newProxy(//将对象启动代理target,targetType===TargetType.COLLECTION?collectionHandlers:baseHandlers)proxyMap.set(target,proxy)//设置指标为代理对象returnproxy//将对象前往进来}
1.3.5.总结一下reactive做了什么?
1.4总结
从源码的角度来说ref自身会判别能否为基本数据类型假设是则是defineProperty成功的假设是复杂数据类型就会依照reactive启动处置,针对不同的数据类型会启动操作,当你ref为对象时会转换reactive对象将代理的对象前往给_value对象,假设是基本数据则会判别能否须要浅层复制,不须要则间接前往了。而reactive这边也会判别是不是基本数据类型是间接前往否则就间接将对象启动了代理并前往。置信本篇文章能够给你带来一些启示。
记一次 Vue2 迁移 Vue3 的实践总结
Vue3中文文档[1]
2.x 全局 API3.x 实例 API (app) 无
引入此配置选项的目的是支持原生自定义元素,因此重命名可以更好地传达它的功能,新选项还需要一个比旧的 string/RegExp 方法提供更多灵活性的函数:
在Vue 2中,通常用于添加可在所有组件中访问的属性。
Vue 3中的等效项是。在实例化应用程序内的组件时,将复制这些属性
2.0生命周期3.0生命周期 beforeCreate(组件创建之前)setup() created(组件创建完成)setup() beforeMount(组件挂载之前)onBeforeMount(组件挂载之前) mounted(组件挂载完成)onMounted(组件挂载完成) beforeUpdate(数据更新,虚拟DOM打补丁之前)onBeforeUpdate(数据更新,虚拟DOM打补丁之前) updated(数据更新,虚拟DOM渲染完成)onUpdated(数据更新,虚拟DOM渲染完成) beforeDestroy(组件销毁之前)onBeforeUnmount(组件销毁之前) destroyed(组件销毁之后)onUnmounted(组件销毁之后) activated(被 keep-alive 缓存的组件激活时调用)onActivated(被激活时执行) deactivated(被 keep-alive 缓存的组件停用时调用)onDeactivated(比如从 A 组件,切换到 B 组件,A 组件消失时执行) errorCaptured(当捕获一个来自子孙组件的错误时被调用)onErrorCaptured(当捕获一个来自子孙组件的异常时激活钩子函数)
注意点:
refreactive入参基本类型引用类型 返回值响应式且可变的 ref 对象响应式代理(Proxy) 访问方式 对象拥有一个指向内部值的单一属性 2.在dom和setup()的return中会自动解套 作为 reactive 对象的 property 被访问或修改时,也将自动解套直接.访问即可
问题 & 注意点: 因为reactive是组合函数【 对象 】,所以必须始终保持对这个所返回对象的引用以保持响应性,不能解构该对象或者展开
例如:
const { a } = objReactive 或者 return { }
解决方法:
用来提供解决此约束的办法——它将响应式对象的每个 property 都转成了相应的 ref【把对象转成了ref】。
watchEffect的第一个参数—— effect 函数——自己也有参数:叫 onInvalidate ,也是一个函数,用于清除effect产生的副作用。
onInvalidate被调用的时机很微妙:它只作用于异步函数,并且只有在如下两种情况下才会被调用:
主要作用是指定调度器,即何时运行副作用函数。
优点:很优秀
缺点:他的对手(React),更优秀
虽然好多地方神似React,但是我们也可以从中看出,他们的都源于比较成熟的编程范式——FP(Functional Programming)。
框架只是工具,解决问题才是终极目标;我们还是要把重点放在领悟框架的设计思想上;悟到了,才是真正掌握了解决问题的手段。(抄的)
基于 Vue3 和 TypeScript 项目大量实践后的思考
Vue3出来已经有一段时间了,在团队中,也进行了大量的业务实践,也有了一些自己的思考。 总的来说,Vue3无论是在底层的原理上,还是在业务的实际开发中,都有了长足的进步。 使用 proxy 代替之前的 的API,性能更加优异,也解决了之前vue在处理对象、数组上的缺陷;在diff算法上,使用了静态标记的方式,大大提升了Vue的执行效率。 在使用的层面,我们从options Api,变成了composition Api,慢慢的在实际的业务中,我们抛弃了原本的data、methods、computed那种隔离式的写法。 compositon Api,它更加聚焦,它讲究的是相关业务的聚合性。 完全良好的支持了TypeScript,类型校验也成为了以后Vue3进行大型项目开发的质量保障,同时这也是面向了趋势 -- 前端的未来就是TypeScript! compositon Api的本质,体现在代码里面,也就是一个setup函数,在这个setup函数中,返回的数据,会用到该组件的模板中。 return的这个对象,一定程度上,代表了之前vue2中的data属性。 这时候,对于大多数初学者来说,可能存在的疑惑就是,那么我能不能定义options Api的写法,比如data、computed、watch、methods等等。 这里我需要明确的是,Vue3是完全兼容Vue2的这种options Api的写法,但是从理念上来说,更加推荐setup的方式,来写我们的组件。 原因如下:Vue3的存在,本身是为了解决Vue2的问题的,Vue2的问题就是在于,聚合性不足,会导致代码越来越臃肿!setup的方式,能够让data、方法逻辑、依赖关系等聚合在一块,更方便维护。 也就是说,以后我们尽量不要写单独的data、computed、watch、methods等等,不是Vue3不支持,而是和Vue3的理念违背。 components属性,也就是一个组件的子组件,这个配置在Vue2和3的差异不大,Vue2怎么用,Vue3依然那么用。 在功能方面,ref 和 reactive,都是可以实现响应式数据! 在语法层面,两个有差异。 ref定义的响应式数据需要用[data]的方式进行更改数据;reactive定义的数据需要[data].[prpoerty]的方式更改数据。 但是在应用的层面,还是有差异的,通常来说:单个的普通类型的数据,我们使用ref来定义响应式。 表单场景中,描述一个表单的key:value这种对象的场景,使用reactive;在一些场景下,某一个模块的一组数据,通常也使用reactive的方式,定义数据。 那么,对象是不是非要使用reactive来定义呢?其实不是的,都可以,根据自己的业务场景,具体问题具体分析!ref他强调的是一个数据的value的更改,reactive强调的是定义的对象的某一个属性的更改。 周期函数,在Vue3中,是被单独使用的,使用方式如下: 在Vue2中,其实可以直接通过this.$store进行获取,但是在Vue3中,其实没有this这个概念,使用方式如下: 在Vue2中,是通过this.$router的方式,进行路由的函数式编程,但是Vue3中,是这么使用的: 这一部分内容,准确的来说,是TS的内容,不过它与Vue3项目开发,息息相关,所以真的想用Vue3,我们还是得了解TS的使用。 不过这一部分,我不会介绍TS的基础语法,主要是在业务场景中,如何组织TS。 在一个常见的接口请求中,我们一般使用TS这么定义一个数据请求,数据请求的req类型,数据请求的res类型。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。