前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CompositionAPI 组件开发范式

CompositionAPI 组件开发范式

原创
作者头像
PY
发布2022-07-17 21:20:16
6090
发布2022-07-17 21:20:16
举报
文章被收录于专栏:PY的开发实践

代码组织

每个贡献者的代码风格都是不同的,在认同 CompositionAPI 理念的情况下。我们希望能够有较为标准的代码组织结构。让整体的代码保持一个大概的代码块风格,组件的维护会更加清晰,避免代码的堆积。

合理的拆分

upload 组件为例,代码块应该是分层设计的。第一步先进行合理的组件拆分,拆分的原则:

  1. 按表现类型拆分出子组件:upload 组件存在多种表现类型,因此会衍生出4个子组件:DraggerImageCardFlowListSingleFile
  2. 按不同的逻辑处理, 内聚出不同的 hook 与表现层代码挂钩:useComponentsStatus, useImgPreview, useRemove, useDragger, useActions

组件状态管理

  1. 组件参数状态,双向绑定语法糖,受控,非受控
  2. 一个组件内置状态的上下文,集中管理组件内置的状态。散落在各个代码块的内部变量难以维护。这样会很清晰的知道组件的内置状态存在哪些。这一部分的代码可以通过 provider 向子组件注入,也可以使用 context 向子组件传递。

注意事项

  • 类似这种简短计算的代码必要性不是很强
代码语言:javascript
复制
const isSingle = computed(() => props.theme === 'single')
  • 组件事件简化,因为 TDAPI 在多个框架下使用,会融合一些 react API 的设计,在事件方面 XXX evnt 通常会伴随一个 onXXX 的函数参数,组件事件
  • options API 中,methods 可以直接暴露出去,不需要 expose,而 setup 中,需要按需 expose 相关函数。
  • 注意逻辑的拆分,不要写长篇代码。做到高内聚,低耦合。

组件代码示例

代码语言:javascript
复制
import { defineComponent } from 'vue';

import { TdUploadProps } from './type'; _// 标准的type文件_

import props from './props'; _// 标准的props文件_

import { xxx } from './interface' _// 如果需要自定义一些 `interface`, 则统一放到 `interface` 文件当中_

_// 子组件_

import Dragger from './dragger';

import ImageCard from './image';

import FlowList from './flow-list';

import SingleFile from './single-file';

_// hooks_

import { useConfig, usePrefixClass, useCommonClassName } from '../hooks/useConfig'; _// 全局的config配置, classPrefix, commonClass_

import useVModel from '../hooks/useVModel'; _// 语法糖与受控处理_

import { useLogicHook } from './hook'; _// 纯逻辑代码的组件内部hook,按逻辑区分,方便后续的维护_

export default defineComponent({

 name: "TUpload",

  props,

 setup(props: TdUploadProps) {

 const { classPrefix: prefix, global } = useConfig('upload');

 const COMPONENT\_NAME = usePrefixClass('upload');

 const { STATUS } = useCommonClassName();

 const { files, modelValue } = toRefs(props);

 _// `files` 的更新统一使用 `setUploadValue`_

 const [uploadValue, setUploadValue] = useVModel(

      files,

      modelValue,

      props.defaultFiles || [],

      props.onChange,

 );

 _// 组件上下文,集中管理组件内置的状态_

 const uploadCtx: UploadCtxType = reactive({

      uploadValue,

      setUploadValue,

 loadingFile: null, _// 加载中的文件_

 toUploadFiles: [], _// 等待上传的文件队列_

 errorMsg: '',

 });

 _// 逻辑层 `hook` 导出表现层需要的变量, 相关的effect函数。_

 const { logicVar, logicHandler } = useLogicHook(props, uploadCtx)

 _// 表现层 `render` 函数, 按模块拆分,避免主 `render` 函数内容过多。_

 const renderContent = () => {

 <div class={[COMPONENT\_NAME.value, {

         STATUS.disabled: props.disabled

 }]} onClick={logicHandler}>

 {logicVar}

 </div>

 }

 _// 可以直接在setup返回render函数,不需要再单独写 `render` 函数。同时 `setup render` 函数里面也有很完整的类型支持。需要对外暴露的方法可以使用 `ctx.expose`_

 return () => (

 <div>{renderContent()}</div>

 )

 },

})

TNode渲染

TNode 介绍

TNodeAPI 中,需要使用 useTNodeJSX 得到渲染函数进行渲染,函数内会处理好 props function props 与插槽的关系。

代码语言:javascript
复制
import { useTNodeJSX } from '../hooks/tnode';

defineComponent({

 setup() {

 const renderTNode = useTNodeJSX();

 const renderChild = () => {

 return renderTNode('default')

 }

 return () => (

 <div>

        // 两种写法

 {renderTNode('TNodeName', options)}

 {renderChild()}

 </div>

 )

 },

})

组件库配置项

统一使用 useConfig, useConfig 会导出 global, classPrefix, t

在很多情况下你可能只需要导出一个带prefix的类名,你可以使用 usePrefixClass

代码语言:javascript
复制
const COMPONENT\_NAME = usePrefixClass('componentName');

_// 也可以只得到一个classPrefix_

const classPrefix = usePrefixClass();

commonClass 集合了一些公用的 class。分为 SIZESTATUS

代码语言:javascript
复制
const { SIZE, STATUS } = useCommonClassName()

ConfigReceiverMixins 会被废弃。

双向绑定语法糖,受控,非受控

逐渐放弃使用高阶函数 mapProps。实现 v-model 使用 useVModel, 实现 v-model:xx 使用 useDefaultValue。这两个 hook 在内部会处理好受控与非受控,组件内部使用暴露出来的值即可,同时对外的参数更新也需要使用暴露出的函数进行更新。

useVModel

用于实现主参数的双向绑定 v-model,受控与非受控

代码语言:javascript
复制
import useVModel from '../hooks/useVModel';

const { value, modelValue } = toRefs(props)

const [innerValue, setInnerValue] = useVModel(

  value,

  modelValue,

  props.defaultValue,

  props.onChange,

 'propsName' _// 可选参数,用于类似 `files` 这种别名主双向绑定参数的处理_

);

useDefaultValue

用于实现辅助参数的双向绑定v-model:visible,受控与非受控

代码语言:javascript
复制
import useDefaultValue from '../hooks/useDefaultValue';

const { visible } = toRefs(props)

const [innerVisible, setInnerVisible] = useDefaultValue(

  visible,

  props.defaultVisible,

  props.onVisibleChange,

 'visible',

);

组件事件

TDesign vue 中事件都会存在 onXXXprops 函数,可以通过 props.onXXX 的方法进行处理。对于 props 中定义了的事件不需要再调用 emit('xxx').

代码语言:javascript
复制
_// props_

{

  onChange?: (...args) => {};

}

_// tsx_

setup(props) {

  props.onChange?.(args)

}

Provide与inject

  1. 合理使用 Provideinject,按需 provide,避免 children 调用 $parent 这类代码.
  2. 合理的 InjectionKey,需要注意 InjectionKey 的导出位置,避免循环引用。
代码语言:javascript
复制
import { InjectionKey } from 'vue';

const CheckboxGroupInjectionKey: InjectionKey<{

  name: string;

  componentProps: string,

  xxxProvideProps: string,

}> = Symbol('componentName');

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码组织
    • 合理的拆分
      • 组件状态管理
        • 注意事项
          • 组件代码示例
          • TNode渲染
          • 组件库配置项
          • 双向绑定语法糖,受控,非受控
            • useVModel
              • useDefaultValue
              • 组件事件
              • Provide与inject
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档