CompositionAPI 项目实战总结
最近公司有个全新的详情,采用 Vue2 + CompositionAPI
编写完成,中间尝试集成过 TSX
,总结遇到的坑及迷惑点
遇到的一些问题
- 直接使用
reactive
的key,用来传递,值并不是响应式的,必须使用computed
包一层 - 如果当前组件在
slot
中,ref
将会失效,模板Ref ,官方暂时没找到解决方案 #296 - 使用
TSX/JSX
官方没有给出,组件实例怎么对外暴露方法 - 需要具备一定的 Ts 类型编程能力,才能较好得封装
useXXX
- 关于全局注册的组件,如
vue-router
vuex
方法给出的使用方案 插件开发 依赖注入 - 各种
useXXX
是依赖setup
上下文的,只能放在setup
里立即执行到,vue 才能收集到依赖 CompositionAPI
侵入性太强,由于所有值都需要包
直接使用reactive
的key,给方法传递参数
如下,直接将 mobile
传递给 useSendCode
,useSendCode
拿到的参数值,将永远是空字符串,因为 mobile
并不是一个响应式对象
const formData = reactive({
name: '',
mobile: ''
})
// 将手机号传递给发送验证码的方法
const { sendCode, isSend } = useSendCode(formData.mobile)
正确写法是,必须要使用computed
包一层
const formData = reactive({
name: '',
mobile: ''
})
// 正确写法,后续拿到 调用 sendCode 时,sendCode 内部使用 unref 解包,拿到对应值
const { sendCode, isSend } = useSendCode(computed(() => formData.mobile))
如果当前组件在 slot
中,ref
将会失效
使用过程中遇到个很低级的Bug,使用ref
来拿到组件实例时,如果当前组件是在 slot
中,则 ref
始终为null
, 官方暂时没找到解决方案 #296
如下示例,在页面中,refHeader
refElement
始终为null
,但是打开调试控制台,能看到 $refs
下是有值的
// 父组件
<div class="PageContainer" :onLoad="handleLoad">
<slot v-if="loading"></slot>
</div>
// 当前页面
<PageContainer>
<Header ref="refHeader"></Header>
<div ref="refElement">
</div>
</PageContainer>
官方提供的 CompositionAPI
中获取 ref 的方法
{
setup(){
const refHeader = ref<any>(null)
const refElement = ref<any>(null)
return {
refHeader,
refElement
}
}
}
解决办法
利用 getCurrentInstance
获取到当前vue
实例,如果自带的 ref 无法获取到值,就取 $refs
上的
import { Ref, ref, getCurrentInstance, computed } from '@vue/composition-api'
export function useRef<T extends any> (key: string): Ref<T> {
const _this = getCurrentInstance() as any
const refComponent = ref<T>()
return computed({
get () {
return refComponent.value || _this.$refs[key]
},
set (newValue) {
refComponent.value = newValue
}
})
}
使用 TSX/JSX
官方没有给出,组件实例怎么对外暴露方法
vue3
中写TSX/JSX
会占用setup
的返回值,外部将无法通过 $ref
调用到组件中的方法,如下
{
setup(){
let message = ref('Hello World')
return () => {
return <div>{message.value}</div>
}
}
}
各种useXXX
是依赖setup
上下文的
当我们自己封装 useXXX
时,内部 onMounted
onUnmounted
等 hooks
,这些方法只能放在setup
方法下,不能放在 setup
会异步执行的函数下。
如下
{
setup(){
// 正确√
const refMenu = useRef('refMenu')
// 错误 ×
function handleLoad(){
// handleLoad 会异步调用,不能放在这里
const refMenu = useRef('refMenu')
return getAjax('/xxx')
.then(() => xxx)
}
return {
refMenu,
handleLoad
}
}
}
CompositionAPI
侵入性太强
虽然官网已经说了 引入-ref-的心智负担 ,但是由于我们的项目用到了大量过去项目积累下的业务库,如果有出现频繁的函数需要传递多个参数(4-5个),并且这个函数会在多个地方调用,就会看到一大片 unref
doPay({
name:unref(name),
url:unref(url),
sex:unref(sex),
...
})
// 我们可能需要一个中间层,来封装旧的工具库,而不是 setup 中频繁 unref
function usePay({name,url,sex}){
return {
pay(){
return doPay({
name:unref(name),
url:unref(url),
sex:unref(sex)
})
}
}
}
关于 useXXX 写法的思考
与传统 ClassApi
编写思路其实没差,由于需要手动对外暴露返回值和方法,相对于 ClassApi
可能会更加斟酌一下,这个值 or 方法是否值得暴露,而不是像 ClassApi
无脑对外暴露
一些代码组织方面的问题
在 vue2 时代,函数,数值,计算属性,放在那,vue 都帮我们规划好了,但是会出现同一片业务代码,出现在好几个地方,
CompositionAPI 虽然宣传说解决了这个问题,但是实际,我们写业务代码,各个变量以及函数之间实际是有关联的,一个变量可能会被多个地方使用,我们按照官方提供的这个图,将 useXXX 返回的业务代码就近,放在与它相关的业务代码下之上,但是可能会随着业务的迭代,它的返回值会被另一处业务所引用,你可能需要挪动 useXXX 所放置的位置,并且这个动作,将会频繁出现在你编写一个新项目的时候
本文由 juzisang 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Aug 7, 2020 at 02:53 pm