15 分钟掌握vue-next函数式api(小结)
写在前面
在分享vue-next各个子模块的实现之前,我觉的有必要比较全面的整理下vue-next中提出的函数式api,了解这些的话,无论是对于源码的阅读,还是当正式版发布时开始学习,应该都会有起到一定的辅助作用。
类似的东西在网上有很多,只是会比较零碎,同时有些也相对过时了,当然当前整理的这些也有可能会过时,毕竟代码还处于pre-alpha的阶段,但其中的设计思想应该是不会改变了。
往期文章
15分钟掌握vue-next响应式原理
vue-next/runtime-core源码阅读指南
setup
setup会作为编写组件业务逻辑的主战场,各种hook类型的方法均需要在setup这个作用域下调用,直接来看RFC中的例子:
其代码结构看起来和vue2基本保持一致,不过有以下几点需要注意:
- setup本身的调用时机,从目前的源码来看,介于beforeCreate和created这两个生命周期之间,言外之意,就是你无法在这里使用this指向当前组件实例
- 对于state的声明,我们使用reactive这个api来构造,对于computedstate的声明,使用computed,两者本质上均是Proxy对象
- 对于组件方法的声明,我们直接通过声明普通函数的方式进行声明即可,对于state的变更,直接通过闭包使用reactive返回的Proxy对象即可
- setup的返回值被称作rendercontext,顾名思义,就是模板中可以访问到的各种数据和方法的上下文对象,我在之前的文章中曾提及,模板在解析数据时,会优先考虑从data对象取值,之后就是这个rendercontext了
- 除了返回rendercontext,还可以返回模板渲染函数,对,就是那个h(...),这种形式对应的情况是,当我们没有声明template属性时
在vue-next中,我们直接从vue导入reactive、computed以及其他的api即可,如果在vue2版本上,我们还可以通过使用composition-api这个plugin来使用这些api。
state
声明state主要有以下几种形式。
基础类型
基础类型可以通过ref这个api来声明,如下:
import{ref}from"vue"; exportdefault{ setup(){ constcount=ref(0); functioninc(){ count.value++; } return{count,inc}; } };
之所以要通过ref,是因为js中的基础类型传值不是引用传值,因此vue-next内部会自动将它封装为一个ref对象,其值指向原始值。当然,ref指向引用类型也是没有问题的,其value指向引用类型变量的引用。
引用类型
引用类型除了可以使用ref来声明,也可以直接使用reactive,如下:
import{reactive}from"vue"; exportdefault{ setup(){ conststate=reactive({ count:0 }); functioninc(){ state.count++; } return{state,inc}; //或者通过toRefs方法 //return{...toRefs(state),inc}; } };
这里使用toRefs的原因主要是因为,如果是reactive产生的对象,由于我们要只是保存对于该Proxy对象的引用,我们无法使用解构来将它flat,而toRefs会将每一个属性在内部包裹为一个ref对象。
props
对于prop可以通过如下代码声明及使用:
exportdefault{ props:{ count:Number }, setup(props){ watch(()=>{ console.log(\`countis:\`+props.count) }) } }
这里可以发现其实和vue2中声明的方式基本一样,但值得注意的是,在vue-next中,props的类型声明不是必须的,如果你使用typescript,完全可以改写为如下的版本:
interfacePropTypesI{ count:number } exportdefault{ setup(props:PropTypesI){ watch(()=>{ console.log(\`countis:\`+props.count) }) } }
除此之外,还可以直接通过watch方法来观察某个prop的变动,这是为什么呢?答案非常简单,就是props本身在源码中,也是一个被reactive包裹后的对象,因此它具有响应性,所以在watch方法中的回调函数会自动收集依赖,之后当count变动时,会自动调用这些回调逻辑。
context
在setup那一小节中,我们知道,setup在调用时,组件实例还未创建,那意味着我们无法使用this访问当前实例,那我想通过this在vue2中访问一些内置属性,怎么办?比如attrs或者emit。我们可以通过setup的第二个参数:
setup(props,context){ doanything... }
这个context对象也是一个Proxy对象,当我们通过context.attrs访问其属性时,本质上代理对象会将访问指向组件的内部实例(即之间文章中提及的componentInternalInstance)。
生命周期
每一个vue2中的组件生命周期函数,当前都对应一个生命周期hook,比如:
import{onMounted,onUpdated,onUnmounted}from"vue"; setup(){ onMounted(()=>{...}); onUpdated(()=>{...}); onUnmounted(()=>{...}); }
这里值得注意的一点在于,对于beforeCreate和created生命周期,虽然有响应的hook,但是我觉的没有必要单独使用了,因为这些逻辑代码大部分是一些初始化逻辑的代码,直接写在setup方法中即可。
如何复用代码
在这个基础上,复用代码的方式也不再像vue2中的那样,通过mixin或者HOC来达到复用代码的目的,这里稍微说一下,这些复用代码方式中比较显著的缺点有:
- 隐藏了数据来源,主要体现在mixin中
- 会牺牲一些性能,主要体现在HOC中
- 可能会遇到命名冲突问题,主要体现在mixin中
在vue-next中,复用代码的逻辑本质上是利用这些hook来拆分业务逻辑与状态,如果你比较熟悉reacthooks的话,应该很快就能明白我指的是什么意思。如果我们将逻辑都写在setup方法中,很快代码就会变得难以维护,在这方面,我们可以将一些耦合在一起的代码抽离出来,同时以use前缀命名,声明一个自定义的hook,如下:
exportdefault{ setup(){ constdata=useSearch() const{sortedData,sort}=useSorting(data) return{data,sortedData,sort} } } functionuseSearch(){ ...fetchdata } functionuseSort(data){ ...sortdata }
除了以inline的方式来声明,还可以将这些自定义的hook声明在单独的文件中,直接通过import导入即可,比如:
importuseSearchfrom'@hooks/search' importuseSortfrom'@hooks/sort'
与reacthooks对比
vue-next在这方面借鉴了reacthooks的设计思想,但是从实现层来讲,它们是不一样的,主要有以下几点:
- vue-next不依赖于其调用顺序,而react依赖
- vue-next提供了生命周期方法,而react刻意模糊生命周期的概念
- vue-next基于响应式系统实现,意味它的依赖不需要显示声明(而且是自动的),而react需要手动声明依赖数组
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。