🦉 Owl Component 🦉
概述
Owl 组件是一个小的类,代表用户界面中的某一部分。 它是组件树的一部分,并拥有一个从父组件向子组件传递的 环境 (env
)。
Owl 组件通过继承 Component
类来定义。例如,下面是一个 Counter
组件的实现方式:
const { Component, xml, useState } = owl;
class Counter extends Component {
static template = xml`
<button t-on-click="increment">
Click Me! [<t t-esc="state.value"/>]
</button>`;
state = useState({ value: 0 });
increment() {
this.state.value++;
}
}
在这个例子中,我们使用 xml
辅助函数定义内联模板,使用 useState
钩子来创建响应式状态(详见响应式一节)。
属性和方法
Component
类的 API 非常小巧:
env (object)
:组件的 环境props (object)
:由父组件传递给子组件的 props 对象。注意:
props
是由父组件拥有的,不属于子组件。因此绝对不能在子组件中修改props
,否则可能造成未预期的副作用(因为父组件不会意识到这种变化)。当父组件动态修改
props
时,子组件将会依次触发生命周期方法:willUpdateProps
、willPatch
和patched
。render(deep[=false])
:手动调用此方法将触发重新渲染。 由于使用了响应式系统,通常不需要手动调用该方法。 渲染是异步的,DOM 的更新会稍后(通常在下一帧)发生。默认情况下,只有当子组件的 props(浅层比较)变化时,才会渲染子组件。 如需强制子组件全部渲染,可使用布尔值
true
作为deep
参数。
静态属性
template (string)
:组件的模板字符串。可使用xml
辅助函数方便地定义内联模板。components (object, optional)
:如果组件包含子组件,可通过该对象注册子组件类:jsclass ParentComponent extends owl.Component { static components = { SubComponent }; }
props (object, optional)
:用于描述接收到的 props 的类型与结构。在dev
模式下将进行 props 校验。详见 props 验证。jsclass Counter extends owl.Component { static props = { initialValue: Number, optional: true, }; }
defaultProps (object, optional)
:用于定义 props 的默认值。如果某个 props 未传递,则使用该默认值。注意默认值不会修改原始 props 对象,而是生成一个新对象。详见 默认 props。jsclass Counter extends owl.Component { static defaultProps = { initialValue: 0, }; }
生命周期
一个健壮的组件系统需要完善的生命周期机制,Owl 提供了如下生命周期方法:
方法 | 钩子函数 | 说明 |
---|---|---|
setup | 无 | 组件构造后立即执行。 |
willStart | onWillStart | 异步钩子,首次渲染前调用,可用于初始化异步数据。 |
willRender | onWillRender | 模板渲染前调用。 |
rendered | onRendered | 模板渲染后调用(DOM 尚未更新)。 |
mounted | onMounted | 渲染并添加到 DOM 后调用。 |
willUpdateProps | onWillUpdateProps | 异步钩子,更新 props 之前调用。 |
willPatch | onWillPatch | DOM 更新前调用,适合读取当前 DOM 状态。 |
patched | onPatched | DOM 更新后调用,适合执行额外 DOM 操作。 |
willUnmount | onWillUnmount | 组件即将从 DOM 移除时调用。 |
willDestroy | onWillDestroy | 组件销毁前调用,始终会执行。 |
onError | onError | 捕捉并处理子组件错误。详见 错误处理。 |
以下是各生命周期方法的简要说明与示例(略去重复代码):
setup
初始化逻辑、注册钩子的地方。相当于构造函数。
setup() {
useSetupAutofocus();
}
willStart
异步初始化,适合加载远程数据、外部库等。首次渲染前只调用一次。
setup() {
onWillStart(async () => {
this.data = await this.loadData();
});
}
willRender
在模板函数执行前调用。
setup() {
onWillRender(() => {
// 执行一些逻辑
});
}
rendered
模板渲染后调用,但此时 DOM 尚未更新。
setup() {
onRendered(() => {
// 模板渲染完毕
});
}
mounted
组件已插入 DOM。适合操作 DOM、添加事件监听等。
setup() {
onMounted(() => {
// 执行一些 DOM 相关逻辑
});
}
willUpdateProps
异步钩子,props 更新前调用。适合根据新 props 执行任务(如获取数据)。
setup() {
onWillUpdateProps(nextProps => {
return this.loadData({ id: nextProps.id });
});
}
willPatch
DOM 更新前调用,适合读取当前 DOM 状态。
setup() {
onWillPatch(() => {
this.scrollState = this.getScrollState();
});
}
patched
DOM 更新后调用。可执行额外 DOM 操作或触发动画。
setup() {
onPatched(() => {
this.scrollState = this.getScrollState();
});
}
willUnmount
组件即将从 DOM 中移除时调用。适合移除事件监听。
setup() {
onMounted(() => { /* 添加监听 */ });
onWillUnmount(() => { /* 移除监听 */ });
}
willDestroy
组件销毁前调用。即使未挂载也会调用,适合做彻底清理。
setup() {
onWillDestroy(() => {
// 清理逻辑
});
}
onError
用于捕捉子组件错误,执行恢复逻辑。
setup() {
onError(() => {
// 错误处理
});
}
子组件
组件可组合子组件构建复杂 UI。在模板中使用以大写字母开头的标签表示子组件,并通过 components
静态属性注册。
class Child extends Component {
static template = xml`<div>child component <t t-esc="props.value"/></div>`;
}
class Parent extends Component {
static template = xml`
<div>
<Child value="1"/>
<Child value="2"/>
</div>`;
static components = { Child };
}
动态子组件
使用 t-component
指令实现动态选择子组件:
class A extends Component {
static template = xml`<div>child a</div>`;
}
class B extends Component {
static template = xml`<span>child b</span>`;
}
class Parent extends Component {
static template = xml`<t t-component="myComponent"/>`;
state = useState({ child: "a" });
get myComponent() {
return this.state.child === "a" ? A : B;
}
}
status
辅助函数
用于判断组件当前状态:
const { status } = owl;
console.log(status(component));
// 返回值可能是:
// - 'new':尚未挂载
// - 'mounted':已挂载
// - 'cancelled':即将被销毁但尚未挂载
// - 'destroyed':已销毁