开发指南/开发工作台小组件/小组件框架/生命周期
# 生命周期
## 简介
当 Creator 或者 Block 被加载时，都会有各自的生命周期（每个生命周期将会有一系列钩子会被触发），开发者可以基于这些生命周期钩子实现自己的业务逻辑。

![image.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/b93d1dabede339ad3b4eab8fc3f1f34e_nEcClx6fsG.png?lazyload=true&width=1640&height=723)

目前支持的生命周期钩子如下表所示。
[Creator](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/block/guide/directory-structure) 和 Block 的生命周期有一定差异，具体请看下面表格中**支持类型**列的说明。

| 生命周期钩子                                                                         | 支持类型              | 说明                                                                                      |
| ---------------------------------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------- |
| onLoad                                                                       | `Block` `Creator` | Block 或 Creator 创建时，触发的第一个生命周期；                                                          |
| onShow                                                                       | `Block` `Creator` | Block 或 Creator 视图可见时触发（依赖宿主策略，默认情况下，宿主页面显示时会触发）；如果 Creator 无界面，则不触发；                     |
| onReady                                                                      | `Block` `Creator` | Block 或 Creator 首屏渲染完成后触发；如果 Creator 无界面，则不触发；                                            |
| onHide                                                                       | `Block` `Creator` | Block 或 Creator 视图隐藏时触发（依赖宿主策略，默认情况下，宿主页面隐藏时会触发）；如果 Creator 无界面，则不触发；                     |
| onDestroy                                                                    | `Block` `Creator` | Block 或 Creator 销毁时会触发；                                                                  |
| onResourceChange                                                             | `Block`           | 用于接收协同数据的变化，应用服务端主动推送必要的信息给 Block 实例时触发；                                                 |
| onInactivate | `Block`           | **仅 PC 环境支持**。Block 框架检测到 Block 逻辑层无运行需求时，会自动进行失活操作，释放内存数据降低内存消耗，开发者需要在此生命周期中利用`tt.saveState`保存数据，以免数据丢失。下面会详细介绍 Block 激活态； |
| onActivate   | `Block`           | **仅 PC 环境支持**。Block 框架检测到 Block 逻辑层存在运行需求时，会自动进行激活，开发者可以在该生命周期获取到`tt.saveState`保存的数据，进行相关状态的恢复操作。下面会详细介绍 Block 失活态；         |
## 激活和失活

在同一个Lark套件页面中，可能存在加载大量小组件的业务场景，为了合理控制内存开销，小组件框架会根据一定的策略进行激活或失活。

- 当小组件失活时，框架会销毁逻辑层（JS Runtime）的运行环境，开发者存放在内存的数据都会被清除，会触发对应的生命周期 `onInactivate`。小组件开发者可以通过 `tt.saveState` 在该生命周期内保存状态，确保下次被激活时能正常恢复状态。

- 当小组件激活时，框架会重新创建逻辑层运行环境，并触发对应生命周期 `onActivate`，该生命周期会携带上次缓存的状态数据。开发者需要基于该状态数据来恢复状态，以此确保与失活前状态一致。
- 仅 PC 环境支持激活、失活生命周期钩子。
- 仅 Block 支持激活、失活生命周期钩子，Creator 不会触发激活和失活。 warning
逻辑层的**激活**与**失活**行为在**任何场景**下都**不保证**被触发，因此请不要依靠相关生命周期完成任何**业务相关**的程序逻辑。

## Block 激活态
### 触发策略

激活的基本原则是存在消息需要被发送到业务逻辑层，下列场景均**有可能**触发激活：

1.  用户鼠标 hover 在小组件上。
1.  用户鼠标 focus 在小组件上，例如存在输入场景时。
1.  移动端 touchstart。
1.  宿主事件消息推送，包括协同推送、事件监听 API。
1.  视图层定时消息，比如 swiper 轮播。

### 注意事项

- 激活状态下，不会触发 onLoad，onLoad 仅在首次启动会触发执行。
- 该生命周期可能触发 0 次，也可能触发 N 次，不要依赖该生命周期进行业务操作，**仅做状态恢复相关的逻辑**。

### 恢复状态
#### 操作方式

当逻辑层被重新激活时会触发该生命周期，第一个参数是失活时缓存的状态，开发者可以根据该状态恢复相关数据逻辑。
小组件激活时恢复的状态主要分为 2 种情况：

-   **数据**，即失活时使用 `tt.saveState` 缓存的数据，开发者应根据该缓存数据恢复相关状态。
-   **事件监听**，由于失活时整个运行环境都会被销毁，因此开发者调用 API 发起的监听也会被清理掉。因此在激活后，需要开发者重新调用相关 API 重新监听。
```javaScript
function init() {
  tt.onViewResize(console.log);
}
Block({
  onLoad() {
    init();
  },
  onActivate(activeState) {
    this.setData(activeState);  // 恢复数据
    init();  // 恢复事件监听
  },
});
```
#### 操作建议

关于恢复状态，建议保持**幂等操作**，即无论何时调用或者重复调用，结果都是一致的，不会有副作用。具体包括如下建议：

-   数据，如果 Block 除 `this.data` 外有其它的业务数据，建议统一管理。
-   事件监听，建议开发者将幂等的初始化操作（事件监听等）抽离为独立的方法，在 `onLoad` 及 `onActivate` 都调用执行。
-   确保在 onActivate 里面基于 tt API 的事件监听注册流程以同步形式进行，否则当外部行为发生改变触发相关事件时，可能无法找到对应的事件回调。

- Good Case: onActivate 内同步注册事件监听。
	```javascript
	Block({
  		onActivate(activeState) {
    	this.setData(activeState);  // 恢复数据
    	tt.onThemeChange(() => {});
  	},
	});
	```
	- Bad Case: onActivate 内异步注册事件监听。
	```javaScript
	Block({
	  onActivate(activeState) {
	    this.setData(activeState);  // 恢复数据
	    setTimeout(() => {
	    	tt.onThemeChange(() => {})
	    });
	  },
	});
	```

## Block 失活态
### 触发策略

失活过程中会有异步任务检测机制，确保异步任务完成后才最终失活。下列场景均**有可能**会触发失活：

1.  首次启动渲染完成后。
1.  鼠标离开小组件范围，触发失活（会和 focus 配合判断）。
1.  Block blur（对应 focus），触发失活（会和 hover 配合判断）。
1.  移动端 touchend 之后一段时间内，无再次操作。
1.  宿主事件消息推送完成一段时间后。

### 注意事项
- 该生命周期内，不允许存在异步任务。
- 在该生命周期内**仅只做保存状态的操作**即可，**不要进行任何删除状态的操作**，包括不限于取消事件监听。
### 缓存状态
#### 操作方式
当逻辑层被清理前会触发该生命周期，开发者需要对当前逻辑层的状态进行缓存。

```javaScript
Block({
  onInactivate() {
    tt.saveState({
      state: this.data,  // 开发者需要缓存的状态
    });
  },
});
```

其中，`state` 可以是任何可序列化的数据，包括但不限于 this.data，比如 Block 内其他的临时变量状态等。

#### 操作建议
-   为了开发者更好地处理缓存和恢复状态，建议将状态集中收敛，尽量避免零散的局部变量来记录状态。
-   小组件框架会自动检测异步任务，比如 Promise、setTimeout 等，会确保异步任务结束后再失活，开发者不需要特殊处理异步任务期间的状态。
-   异步任务尽量使用 Promise，避免使用回调形式处理。
-   失活时，基于 tt API 的事件监听，不要进行手动释放，因为引擎需要维持监听的行为，确保外部环境发生变化时，能正常触发激活操作。
