Skip to content

子组件

相对页面组件而言,子组件有三件套:PropsEmitsSlots。那么在 Zova 中子组件的三件套如何定义和使用呢?

创建子组件

我们先通过一个 cli 命令来创建一个子组件card:

bash
$ zova :create:component card --module=a-demo

该命令会创建一个目录src/component/card。在 Zova 中,一个子组件被切分为四个文件,位于刚才创建的目录中:

src
└─ component
   └─ card
      ├─ index.vue
      ├─ controller.ts
      ├─ render.tsx
      └─ style.ts
名称说明
index.vue用于定义vue组件
controller.ts用于业务逻辑的 local bean
render.tsx用于组件渲染的 local bean
style.ts用于组件样式的 local bean

Props

接下来,在card子组件中,定义三个 Props:headercontent、和 footer

定义Props接口

首先,在controller.ts中定义 Props 接口:

typescript
export interface Props {
  header?: string;
  content?: string;
  footer?: string;
}

还可以为 Props 设置缺省值:

typescript
export class ControllerCard {
  static $propsDefault = {
    header: 'default header',
  };
}

定义组件Props

然后,在index.vue中定义组件 Props:

typescript
<script setup lang="ts">
import { ControllerCard, Props} from './controller.js';
const props = withDefaults(defineProps<Props>(), ControllerCard.$propsDefault);
</script>

访问Props

render.tsx中访问 Props:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <div>
          <div style={{ backgroundColor: 'teal' }}>
            <div>Prop: {this.$props.header}</div>
          </div>
          <div style={{ backgroundColor: 'orange' }}>
            <div>Prop: {this.$props.content}</div>
          </div>
          <div style={{ backgroundColor: 'green' }}>
            <div>Prop: {this.$props.footer}</div>
          </div>
        </div>
      </div>
    );
  }
}

使用Props

接下来,在父组件中使用子组件:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          header="header"
          content="content"
          footer="footer"
        ></Card>
      </div>
    );
  }
}
  • index.vue导入子组件Card,然后直接给 Card 的 props 传值即可

Emits

接下来,在card子组件中,定义一个 Emit:reset

定义Emits接口

首先,在controller.ts中定义 Emits 接口:

typescript
export type Emits = {
  (e: 'reset', time: Date): void;
};

定义组件Emits

然后,在index.vue中定义组件 Emits:

typescript
<script setup lang="ts">
import { Emits } from './controller.js';
const emit = defineEmits<Emits>();
</script>

触发Emit

render.tsx中触发 Emit:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <button
          onClick={() => {
            this.$emit('reset', new Date());
          }}
        >
          Reset Time
        </button>
      </div>
    );
  }
}

使用Emits

接下来,在父组件中使用子组件:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          onReset={time => {
            console.log(time);
          }}
        ></Card>
      </div>
    );
  }
}
  • index.vue导入子组件Card,然后向onReset传入事件回调函数即可

Slots

接下来,在card子组件中,定义三个 Slots:headerdefaultfooter

定义Slots接口

首先,在controller.ts中定义 Slots 接口:

typescript
export interface Slots {
  header?(): JSX.Element;
  default?(): JSX.Element;
  footer?(): JSX.Element;
}

渲染Slots

render.tsx中渲染 Slots:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <div>
          <div style={{ backgroundColor: 'teal' }}>
            <div>Slot: {this.$slots.header?.()}</div>
          </div>
          <div style={{ backgroundColor: 'orange' }}>
            <div>Slot: {this.$slots.default?.()}</div>
          </div>
          <div style={{ backgroundColor: 'green' }}>
            <div>Slot: {this.$slots.footer?.()}</div>
          </div>
        </div>
      </div>
    );
  }
}

使用Slots

接下来,在父组件中使用子组件:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          slots={{
            header: () => {
              return <div>this is a header slot from parent</div>;
            },
            default: () => {
              return <div>this is a default slot from parent</div>;
            },
            footer: () => {
              return <div>this is a footer slot from parent</div>;
            },
          }}
        ></Card>
      </div>
    );
  }
}
  • index.vue导入子组件Card,然后直接给 Card 的 slots 属性传值即可

如何引用子组件实例

在 Zova 中,不使用Template Ref引用子组件实例,而是直接引用子组件对应的controller bean,参见:Controller Ref

通过模块Scope使用子组件

在 Zova 中,除了一般的导入组件的使用方式之外,还可以通过模块 Scope 对象来直接使用子组件,参见:Vue子组件

基于 MIT 许可发布