See how Stencil fits into the entire Ionic Ecosystem ->
Stencil is part of the Ionic Ecosystem ->

组件生命周期方法

组件有许多生命周期方法,可用于了解组件何时“即将”和“已经”加载、更新和渲染。这些方法可以添加到组件中,以便在正确的时间挂钩操作。

在组件类中实现以下方法之一,Stencil 将自动以正确的顺序调用它们:

componentDidLoad()componentDidUpdate()disconnectedCallback()@Watch(‘propName’)render()connectedCallback()componentShouldUpdate()componentWillRender()componentWillUpdate()componentDidRender()componentWillLoad()Component initializedChange in a value of prop or state triggers rerenderComponent removedComponent reattached

connectedCallback()

每次组件连接到 DOM 时调用。 当组件第一次连接时,这个方法会在 componentWillLoad 之前调用。

需要注意的是,这个方法可以被多次调用,每次元素在 DOM 中都被 attached 或​​ moved 时都会调用。对于每次在 DOM 中附加或移动元素时都需要运行的逻辑,使用此生命周期方法被认为是最佳实践。

const el = document.createElement('my-cmp');
document.body.appendChild(el);
// connectedCallback() 调用
// componentWillLoad() 调用 (第一次)

el.remove();
// disconnectedCallback()

document.body.appendChild(el);
// connectedCallback() 再次调用, 但是 `componentWillLoad()` 并没有调用。

这个 lifecycle 钩子遵循与 Custom Elements Spec 所描述的含义相同。

disconnectedCallback()

每次组件从 DOM 断开连接时调用,即它可以被分派多次,不要与“onDestroy”类事件混淆。

这个 lifecycle 钩子遵循与 Custom Elements Spec 所描述的含义相同。

componentWillLoad()

在组件第一次连接到 DOM 之后调用一次。由于此方法只调用一次,因此是异步加载数据的好地方。

可以返回一个 promise,可用于等待第一次渲染完成。

componentDidLoad()

在组件完全加载并第一次调用 render() 后调用一次。

componentShouldUpdate()

当组件的 PropState 属性更改并且即将请求重新渲染时,将调用此钩子。这个钩子接收三个参数:新值、旧值和改变状态的名称。它应该返回一个布尔值来指示组件是否应该重新渲染(true)或(false)。

需要注意的几件事是,此方法不会在初始渲染之前执行,即当组件第一次渲染到 dom 时,也不会在下一帧执行。

假设组件的以下两个 props 同步更改:

component.somePropA = 42;
component.somePropB = 88;

因为这个钩子的执行可能是有条件的,依靠它来观察 prop 的变化不是最好的方法,最好的方法是使用 @Watch 装饰器。

componentWillRender()

在没次 render() 调用前被调用。

返回一个 promise,可用于等待接下来的渲染。

componentDidRender()

在每次 render() 执行完成后被调用。

componentWillUpdate()

由于某些 Prop()State() 发生了变化,导致组件即将更新时调用。 它永远不会在 render() 期间被调用。

返回一个 promise,可用于等待下一次渲染。

componentDidUpdate()

在组件更新后立即调用。 它永远不会在第一次 render() 期间被调用。

渲染中的状态

始终建议在 componentWillRender() 中进行任何渲染状态更新,因为这是在 render() 方法 之前 调用的方法。或者使用 componentDidLoad()componentDidUpdate()componentDidRender() 方法更新渲染状态将导致再次重新渲染,但是这对性能来说并不理想。

如果状态 必须componentDidUpdate()componentDidRender() 中更新,它有可能使组件陷入无限循环。如果在componentDidUpdate() 中更新状态是不可避免的,那么该方法还应该提供一种方法来检测道具或状态是否“dirty”(数据实际上不同还是与以前相同)。通过进行脏检查,componentDidUpdate() 能够避免渲染相同的数据,从而再次调用componentDidUpdate()

嵌套结构的生命周期

生命周期方法的一个有用特性,它们也考虑了子组件的生命周期。例如,如果父组件 cmp-a 有一个子组件 cmp-b,那么在 cmp-b 完成加载之前,不会将 cmp-a 视为“已加载”。换一种说法就是最深的组件首先完成加载,然后componentDidLoad()调用冒泡。

同样重要的是要注意,即使 Stencil 可以延迟加载组件,并且具有异步渲染,生命周期方法仍然会以正确的顺序调用。因此,虽然顶级组件可能已经加载,但它的所有生命周期方法仍然以正确的顺序调用,这意味着它将等待子组件完成加载。完全相反的情况也是如此,其中子组件可能已经准备就绪而父组件尚未准备就绪。

在下面的示例中,我们有一个简单的组件嵌套。编号列表显示了生命周期方法将触发的顺序。

  <cmp-a>
    <cmp-b>
      <cmp-c></cmp-c>
    </cmp-b>
  </cmp-a>
  1. cmp-a - componentWillLoad()
  2. cmp-b - componentWillLoad()
  3. cmp-c - componentWillLoad()
  4. cmp-c - componentDidLoad()
  5. cmp-b - componentDidLoad()
  6. cmp-a - componentDidLoad()

即使某些组件可能已加载或未加载,整个组件层次结构也会等待其子组件完成加载和渲染。

异步生命周期方法

生命周期方法还可以返回 promise,允许该方法异步检索数据或执行任何异步任务。一个很好的例子是获取要在组件中呈现的数据。例如,您正在阅读的这个站点在呈现之前首先获取内容数据。但是因为fetch() 是异步的,所以componentWillLoad() 返回一个Promise 以确保其父组件在其所有内容已渲染之前不被视为“loaded”,这一点很重要。

下面是一个简单的例子,展示了 componentWillLoad() 如何让它的父组件等待它完成,加载它的数据。

componentWillLoad() {
  return fetch('/some-data.json')
    .then(response => response.json())
    .then(data => {
      this.content = data;
    });
}

举例

这个简单的例子显示了一个时钟并每秒更新一次当前时间。将组件添加到 DOM 时启动计时器。一旦它从 DOM 中移除,计时器就会停止。

import { Component, State, h } from '@stencil/core';

@Component({
  tag: 'custom-clock'
})
export class CustomClock {

  timer: number;

  @State() time: number = Date.now();

  connectedCallback() {
    this.timer = window.setInterval(() => {
      this.time = Date.now();
    }, 1000);
  }

  disconnectedCallback() {
    window.clearInterval(this.timer);
  }

  render() {
    const time = new Date(this.time).toLocaleTimeString();

    return (
      <span>{ time }</span>
    );
  }
}

这是运行的示例。如果您想查看它的实际效果,只需使用开发工具检查它即可。

6:50:34 AM
BackNext
Translators
Contributors