Vue with TSX 的一些爽点

    231

TSX 其实也不是什么很新鲜的东西,Vue 对其的支持也有很长一段时间了,但是!在 Vue 里头写 TSX 实在是太爽了 😱

用 TSX 的一个契机是源自之前封装的表格构造器的单元格渲染,这里一开始我就考虑了,加入了函数 render 来渲染 VNode,这样就能够自定义每个单元格

在没加入 TSX 之前就需要用 h 函数,这里内容可以是

h

import { ElTooltip } from 'element-plus'

/**
 * Renders a user field with avatar, nickname, and phone number.
 * @param item An object containing the user's avatar, nickname, phone, and vId.
 * @param h The virtual DOM function.
 * @returns A virtual DOM node representing the user field.
 */
export const UserField = (
  item: { avatar: string; nickname: string; phone: string; vId?: string },
  h: any
) => {
  return h(
    ElTooltip,
    { content: item.vId || '-', placement: 'top', effect: 'dark' },
    () =>
      h(
        'div',
        {
          class: 'flex items-center gap-[5px]',
        },
        [
          h('img', { src: item.avatar, class: 'w-[45px] h-[45px] rounded-[5px]' }),
          h('div', { class: 'flex flex-col gap-[3px]' }, [
            h('span', { class: 'text-xs text-gray-700' }, item.nickname),
            h('span', { class: 'text-xs text-slate-600' }, item.phone || '-'),
          ]),
        ]
      )
  )
}

不仅读起来比较费劲,写起来更是十分费劲,或者说用模板的方式?
当然也是可以的,不过,模板是一个单独的文件,自定义的单元格越多,.vue 文件就会越多,这种场景下我觉得它并不是最好的选择

TSX

<script lang='tsx' setup>
const DataTableNode = DataTable<TableType>({
  items: tableData,
  ...
  columns: [
    {
      label: '状态',
      render: item => <ItemStatus item={item} />,
    },
  ]
  ...
}

function ItemStatus({ item }: { item: TableType }) {
  const status = StatusValues.find(v => item.recordStatus === String(v.value)) || {
    label: '未知状态',
    bgColor: 'bg-gray-500/10',
    textColor: 'text-gray-500',
  }

  return (
    <>
      <Tag str={status.label} bgColor={status.bgColor} textColor={status.textColor} />
    </>
  )
}
</script>

// 在 <template> 中使用
<template>
  <div class="p-[20px]">
    <DataTableNode />
  </div>
</template>

这里的 DataTable 是一个返回 VNode 的函数

/**
 * Creates a DataTable component with the given table definition.
 * @template T The type of data in the table.
 * @param {TableDefine<T>} define The table definition.
 * @returns {VNode} The DataTable component.
 */
export default <T>(define?: TableDefine<T>) => {
  return h(DataTable, define)
}

About

本质上的优势就是可以在同组件内编写,并且相比直接使用 h 函数要方便很多
另外,对于创建更加复杂的组件,我会选择使用 defineComponentTSX 配合,这样做由于是创建的 Vue 组件,可以使用生命周期函数等等,和传统的 FunctionalComponent 比起来可控性更好,比如:

type Props = {
  item?: TheTypeOfItem
  refresh: () => void
}

const DialogContent = defineComponent({
  props: {
    item: {
      type: Object as PropType<TheTypeOfItem>,
    },
    refresh: {
      type: Function as PropType<() => void>,
    },
    close: {
      type: Function as PropType<() => void>,
      required: true,
    },
  },
  setup(props, ctx) {
    const form = ref<ShopMallStoreRecord>(props.item || {})
    const DataFormNode = DataForm<ShopMallStoreRecord>({
      modelValue: form,
      labelWidth: 80,
      formItems: [
        ...
      ],
      actions: [
        {
          type: 'default',
          text: '返回',
          handler: () => {
            props.close?.()
          },
        },
        {
          type: 'primary',
          text: '提交',
          verify: true,
          show: item => !item.id,
          handler: () => {
            // TODO
          },
        },
      ],
    })

    return {
      DataFormNode,
    }
  },
  render() {
    return <div class="px-[20px]">{this.DataFormNode}</div>
  },
})

const show = (prop?: Props) => {
  const instance = Dialog.init({
    title: 'Dialog Title',
    width: 650,
    content: () => (
      <DialogContent item={prop?.item} refresh={prop?.refresh} close={instance!.close} />
    ),
  })
}

export default { show }
消息盒子
# 您需要首次评论以获取消息 #
# 您需要首次评论以获取消息 #

只显示最新10条未读和已读信息