CompositionAPI 项目实战总结
in Web with 0 comment

CompositionAPI 项目实战总结

in Web with 0 comment

CompositionAPI 项目实战总结

最近公司有个全新的详情,采用 Vue2 + CompositionAPI 编写完成,中间尝试集成过 TSX,总结遇到的坑及迷惑点

遇到的一些问题

直接使用reactive的key,给方法传递参数

如下,直接将 mobile 传递给 useSendCodeuseSendCode 拿到的参数值,将永远是空字符串,因为 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 onUnmountedhooks,这些方法只能放在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 所放置的位置,并且这个动作,将会频繁出现在你编写一个新项目的时候

Responses