Ember组件参数与签名 #
一、组件参数(Args) #
1.1 参数基础 #
组件通过 this.args 访问传入的参数:
javascript
// app/components/user-card.js
import Component from '@glimmer/component';
export default class UserCardComponent extends Component {
get displayName() {
return this.args.user?.name || '匿名用户';
}
}
handlebars
{{! 使用组件}}
<UserCard @user={{@currentUser}} />
1.2 参数特点 #
- 只读:参数不能在组件内部修改
- 响应式:参数变化时组件自动更新
- 可选:参数可以不传递
javascript
export default class ButtonComponent extends Component {
get variant() {
// 参数有默认值
return this.args.variant || 'primary';
}
get disabled() {
// 参数可选
return this.args.disabled ?? false;
}
}
二、组件签名(Signature) #
2.1 定义签名 #
签名用于定义组件的接口类型:
typescript
// app/components/user-card.ts
import Component from '@glimmer/component';
interface UserCardSignature {
// 允许的参数
Args: {
user: User;
size?: 'small' | 'medium' | 'large';
showEmail?: boolean;
onEdit?: (user: User) => void;
};
// 产出块参数
Blocks: {
default: [user: User];
};
// 允许的HTML属性
Element: HTMLDivElement;
}
export default class UserCardComponent extends Component<UserCardSignature> {
// 组件实现
}
2.2 签名组成部分 #
typescript
interface ComponentSignature {
// Args: 组件参数类型
Args: {
[key: string]: any;
};
// Blocks: 产出块参数类型
Blocks: {
default?: [...any];
[blockName: string]: [...any] | undefined;
};
// Element: 根元素类型
Element: HTMLElement | null;
}
2.3 完整签名示例 #
typescript
// app/components/modal.ts
import Component from '@glimmer/component';
interface ModalSignature {
Args: {
isOpen: boolean;
title?: string;
size?: 'small' | 'medium' | 'large';
onClose: () => void;
onConfirm?: () => void;
};
Blocks: {
default: [];
header: [];
body: [];
footer: [];
};
Element: HTMLDivElement;
}
export default class ModalComponent extends Component<ModalSignature> {
get modalClass() {
const classes = ['modal'];
if (this.args.size) {
classes.push(`modal-${this.args.size}`);
}
if (this.args.isOpen) {
classes.push('modal-open');
}
return classes.join(' ');
}
}
三、参数类型 #
3.1 基本类型 #
typescript
interface MyComponentSignature {
Args: {
// 字符串
title: string;
// 数字
count: number;
// 布尔值
isActive: boolean;
// 可选参数
description?: string;
};
}
3.2 对象类型 #
typescript
interface UserCardSignature {
Args: {
user: {
id: string;
name: string;
email: string;
avatar?: string;
};
};
}
3.3 函数类型 #
typescript
interface ButtonSignature {
Args: {
onClick: (event: MouseEvent) => void;
onHover?: (isHovering: boolean) => void;
};
}
3.4 联合类型 #
typescript
interface AlertSignature {
Args: {
type: 'success' | 'warning' | 'error' | 'info';
size?: 'small' | 'medium' | 'large';
};
}
四、块参数 #
4.1 定义块参数 #
typescript
interface ListSignature {
Args: {
items: T[];
};
Blocks: {
// 默认块产出 item 和 index
default: [item: T, index: number];
};
}
export default class ListComponent extends Component<ListSignature<T>> {
<ul>
{{#each @items as |item index|}}
<li>{{yield item index}}</li>
{{/each}}
</ul>
}
4.2 多个块 #
typescript
interface CardSignature {
Blocks: {
default: [];
header: [];
footer: [];
};
}
handlebars
{{! app/components/card.hbs}}
<div class="card">
<div class="card-header">
{{yield to="header"}}
</div>
<div class="card-body">
{{yield}}
</div>
<div class="card-footer">
{{yield to="footer"}}
</div>
</div>
handlebars
{{! 使用}}
<Card>
<:header>
<h2>标题</h2>
</:header>
<p>内容</p>
<:footer>
<button>操作</button>
</:footer>
</Card>
五、元素类型 #
5.1 指定根元素 #
typescript
interface ButtonSignature {
Element: HTMLButtonElement;
Args: {
type?: 'button' | 'submit' | 'reset';
disabled?: boolean;
};
}
handlebars
{{! app/components/button.hbs}}
<button
type={{or @type "button"}}
disabled={{@disabled}}
...attributes
>
{{yield}}
</button>
5.2 splat属性 #
使用 ...attributes 将HTML属性传递给根元素:
handlebars
{{! app/components/button.hbs}}
<button
type={{or @type "button"}}
...attributes
>
{{yield}}
</button>
handlebars
{{! 使用 - 传递额外的HTML属性}}
<Button
type="submit"
class="btn-primary"
id="submit-btn"
data-testid="submit-button"
>
提交
</Button>
5.3 无根元素组件 #
typescript
interface TextSignature {
Element: null; // 无根元素
Args: {
text: string;
};
}
六、参数验证 #
6.1 运行时验证 #
javascript
import Component from '@glimmer/component';
import { assert } from '@ember/debug';
export default class UserCardComponent extends Component {
constructor(owner, args) {
super(owner, args);
assert('user parameter is required', this.args.user);
assert('user must have a name', this.args.user.name);
}
}
6.2 默认值处理 #
javascript
export default class AlertComponent extends Component {
get type() {
return this.args.type || 'info';
}
get dismissible() {
return this.args.dismissible ?? true;
}
get timeout() {
return this.args.timeout ?? 5000;
}
}
七、参数最佳实践 #
7.1 参数命名约定 #
handlebars
{{! 好的命名}}
<UserCard
@user={{@user}}
@size="large"
@showEmail={{true}}
@onEdit={{this.handleEdit}}
/>
{{! 避免}}
<UserCard
@u={{@user}}
@s="large"
@show={{true}}
@edit={{this.handleEdit}}
/>
7.2 布尔参数 #
handlebars
{{! 推荐方式}}
<Input @disabled={{true}} />
<Input @disabled={{false}} />
{{! 简写方式(仅适用于true)}}
<Input @disabled />
7.3 回调参数命名 #
handlebars
{{! 事件回调使用 on 前缀}}
<Button @onClick={{this.handleClick}} />
<Input @onChange={{this.handleChange}} />
{{! 动作回调使用 on 前缀}}
<Modal @onClose={{this.closeModal}} @onConfirm={{this.confirmAction}} />
7.4 可选参数文档 #
typescript
/**
* UserCard 组件
*
* @param user - 用户对象(必需)
* @param size - 卡片大小:small | medium | large(可选,默认 medium)
* @param showEmail - 是否显示邮箱(可选,默认 false)
* @param onEdit - 编辑回调函数(可选)
*/
interface UserCardSignature {
Args: {
user: User;
size?: 'small' | 'medium' | 'large';
showEmail?: boolean;
onEdit?: (user: User) => void;
};
}
八、复杂参数场景 #
8.1 配置对象 #
typescript
interface DataTableSignature {
Args: {
data: any[];
columns: ColumnConfig[];
config?: {
sortable?: boolean;
filterable?: boolean;
pagination?: boolean;
pageSize?: number;
};
};
}
interface ColumnConfig {
key: string;
label: string;
sortable?: boolean;
render?: (value: any, row: any) => string;
}
handlebars
<DataTable
@data={{@users}}
@columns={{this.columns}}
@config={{hash
sortable=true
pagination=true
pageSize=10
}}
/>
8.2 泛型组件 #
typescript
interface ListSignature<T> {
Args: {
items: T[];
renderItem?: (item: T) => string;
};
Blocks: {
default: [item: T, index: number];
};
}
export default class ListComponent<T> extends Component<ListSignature<T>> {
}
九、TypeScript集成 #
9.1 类型安全的组件 #
typescript
// app/components/user-form.ts
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
interface User {
id: string;
name: string;
email: string;
}
interface UserFormSignature {
Args: {
user?: User;
onSubmit: (user: Partial<User>) => void;
onCancel: () => void;
};
}
export default class UserFormComponent extends Component<UserFormSignature> {
@tracked name = this.args.user?.name ?? '';
@tracked email = this.args.user?.email ?? '';
@action
handleSubmit(event: Event) {
event.preventDefault();
this.args.onSubmit({
name: this.name,
email: this.email,
});
}
@action
updateName(event: Event) {
this.name = (event.target as HTMLInputElement).value;
}
@action
updateEmail(event: Event) {
this.email = (event.target as HTMLInputElement).value;
}
}
9.2 类型检查 #
handlebars
{{! TypeScript会检查参数类型}}
<UserForm
@user={{@currentUser}}
@onSubmit={{this.handleSubmit}}
@onCancel={{this.handleCancel}}
/>
{{! 错误示例 - 类型不匹配}}
<UserForm
@user={{123}}
{{! Error: Type 'number' is not assignable to type 'User | undefined'}}
/>
十、总结 #
组件参数与签名要点:
| 概念 | 说明 |
|---|---|
| Args | 组件参数定义 |
| Blocks | 产出块参数定义 |
| Element | 根元素类型 |
| …attributes | HTML属性透传 |
| @tracked | 状态追踪 |
良好的组件接口设计是构建可维护应用的基础。
最后更新:2026-03-28