API
组件 API
TSone 使用面向对象组件模型。每个组件继承 Component<Props, State>,并通过 protected render(): VNode 返回 VNode。
Component<Props, State>
| 名称 | 签名 | 说明 |
|---|---|---|
Component<Props, State> | abstract class Component<Props, State> | 基于类的组件基类,提供 props、state、生命周期和 render。 |
import { Component, VNode } from '@geektech/tsone';
interface CounterProps {
initial?: number;
}
interface CounterState {
count: number;
}
class Counter extends Component<CounterProps, CounterState> {
protected initState(): CounterState {
return {
count: this.props.initial ?? 0,
};
}
protected initStyles(): void {}
protected render(): VNode {
return {
tag: 'button',
listeners: {
click: () => {
this.state.count += 1;
},
},
children: ['count: '],
};
}
}必须实现的方法
protected initState(): State: 返回组件初始状态,状态会被 reactive 包装protected initStyles(): void: 初始化组件样式,可使用 this.styleManagerprotected render(): VNode: 返回虚拟节点
生命周期
beforeMount(): 首次渲染前onMounted(): DOM 挂载后beforeUpdate(): 状态更新触发 patch 前onUpdated(): patch 完成后beforeUnmount(): 卸载前onUnmounted(): 卸载后onErrorCaptured(error, instance): 捕获后代组件渲染或更新抛出的错误,返回 false 停止传播
状态和属性
component.setProps({ initial: 3 });
component.setState({ count: 4 });props: 构造组件时传入,也可由父组件 VNode 的 props 提供state: 响应式状态setProps(props): 合并更新属性,已挂载时触发更新setState(state): 合并更新状态
事件
const unsubscribe = component.on('submit', (payload) => {
console.log(payload);
});
unsubscribe();组件内部可使用 this.emit(eventName, ...args) 触发事件。
在父组件 VNode 上使用 emitters 可声明事件处理器;patch 时旧处理器会被替换或清理。
{
component: Editor,
emitters: {
saved: (payload) => this.save(payload),
},
}依赖注入
使用 InjectionKey、provide 和 inject 在应用和组件树中共享依赖。组件注入按自身、父级、应用顺序查找。
import { InjectionKey } from '@geektech/tsone';
const THEME: InjectionKey<{ mode: string }> = Symbol('theme');
app.provide(THEME, { mode: 'dark' });
const theme = this.inject(THEME, { mode: 'light' });HTML 标签快捷函数
使用 Tag(tag, options) 创建任意 HTML VNode,也可以用具名快捷函数创建常用元素。所有快捷函数都支持相同的 props、children、listeners、key、slot 和 directions 选项。
import { Section, Tag } from '@geektech/tsone';
const panel = Section({ props: { className: 'panel' }, children: ['内容'] });
const dialog = Tag('dialog', { props: { open: true }, children: ['你好'] });常用快捷函数包括 Div、Span、P、Button、Input、Section、Main、Header、Footer、Nav、Article、Aside、H1 至 H6、Strong、Em、Small、Pre、Code、Blockquote、Ul、Ol、Li、A、Img、Form、Label、Textarea、Select、Option、Table、Thead、Tbody、Tr、Th 和 Td。
条件、列表与模型绑定
import { Input, each } from '@geektech/tsone';
const children = each(
this.state.users,
(user) => ({ tag: 'li', children: [user.name] }),
(user) => user.id
);
const field = Input({
directions: {
model: { path: 'profile.name' },
},
});
const panel = {
component: ProfilePanel,
directions: { if: this.state.visible },
};列表进入和退出动画
TransitionGroup 会为初始和新增的带 key 子节点播放进入动画,并把已移除子节点的卸载延迟到退出动画结束。公开动画类型为 TransitionAnimationType。
import { Li, TransitionGroup, each } from '@geektech/tsone';
{
component: TransitionGroup,
props: { tag: 'ul', type: 'fade', duration: 300 },
children: each(
this.state.items,
(item) => Li({ children: [item.label] }),
(item) => item.id
),
}可用类型包括 fade、slide-up、slide-down、slide-left、slide-right 和 scale。tag 默认 div,type 默认 fade,duration 默认 300 毫秒,缓动固定为 ease。直属子节点必须具有唯一 key。组件遵循 prefers-reduced-motion,Web Animations 不可用时也会跳过动画。重排只移动保留节点,不播放重排或 FLIP 动画。
单元素过渡与组件缓存
Transition 在 show 切换时为单个元素播放过渡,应用 {name}-enter-from / {name}-enter-to 与 {name}-leave-from / {name}-leave-to CSS class(各配套一个 -active class),并在离开开始 duration 毫秒后移除元素。KeepAlive 让所有带 key 的子组件保持挂载(状态与 DOM 保留),仅活跃项可见,适合 tab 切换等需要保留状态的场景。
import { KeepAlive, Transition, createComponent } from '@geektech/tsone';
const dialog = {
component: Transition,
props: { show: this.state.open, name: 'fade', duration: 300 },
children: [Dialog({ children: ['设置'] })],
};
const tabs = {
component: KeepAlive,
props: { activeKey: this.state.activeTab },
children: [
createComponent(Editor, {}, [], 'editor'),
createComponent(Preview, {}, [], 'preview'),
],
};表单校验
import { createForm, minLength, required, validate } from '@geektech/tsone';
const form = createForm(this.state, {
'profile.name': [required('请输入姓名'), minLength(2)],
'profile.age': [validate((value) => Number(value) >= 18 || '年龄须不小于 18')],
});
const result = form.validate();
const field = form.validateField('profile.name');
form.resetErrors();Slots
class Panel extends Component<object, object> {
protected initState() {
return {};
}
protected initStyles(): void {}
protected render(): VNode {
return {
tag: 'section',
children: [
{ tag: 'slot', props: { name: 'header' } },
{ tag: 'slot', props: { name: 'default' } },
],
};
}
}