Skip to content

事件

组件向外通知变化时使用事件。宏组件里用 defineEmits() 得到 emit 函数。

ts
import { defineEmits, defineHtml } from "@elfui/core";

interface SearchBoxEmits {
  change: [value: string];
  clear: [];
}

const emit = defineEmits<SearchBoxEmits>(["change", "clear"]);

const onInput = (event: Event): void => {
  const input = event.target as HTMLInputElement;
  emit("change", input.value);
};

export const SearchBox = defineHtml(`
  <input @input=${onInput} />
  <button @click=${() => emit("clear")}>清空</button>
`);

监听事件

父组件模板里监听自定义事件:

ts
const update = (event: CustomEvent<string>): void => {
  keyword.set(event.detail);
};

export const Page = defineHtml(` <search-box @change=${update}></search-box> `);

单参数事件的 detail 就是该参数。多参数事件会以数组形式放入 detail

组件事件是标准 CustomEventbubblescomposedcancelable 默认均为 false;只有事件确实需要参与 DOM 冒泡或穿过 Shadow DOM 边界时才开启:

ts
defineOptions({
  emitOptions: {
    events: {
      submit: { bubbles: true, composed: true, cancelable: true },
    },
  },
});

const accepted = emit("submit", value);

emit() 返回 dispatchEvent() 的布尔结果;可取消事件被宿主 preventDefault() 后返回 false

类型与运行时事件名

有限键的 interfacetype 都可以作为 defineEmits 泛型。同文件声明可以由编译器提取事件名;如果事件类型从另一个文件导入,应同时传入运行时事件名数组:

ts
import type { SearchBoxEmits } from "./types";

const emit = defineEmits<SearchBoxEmits>(["change", "clear"]);

数组中的名称会受泛型键约束,写入不存在的事件会得到 TypeScript 错误。

什么时候用事件

事件适合表达“已经发生的事”,比如 changesubmitclose。如果父组件需要直接控制值,用 v-model 更清晰。