Skip to content
章节目录

响应式数据

当组件的 prop 或 state 发生变化时,Stencil 组件也会更新。

渲染方法

当组件的 props 或 state 发生变化时,render() 方法 将按计划运行。

Watch 装饰器

@Watch() 是一个应用于 Stencil 组件的方法的装饰器。该装饰器接受一个参数,即用 @Prop()@State() 装饰的类成员的名称。 使用 @Watch() 修饰的方法将在其关联的类成员发生更改时自动运行。

tsx
// We import Prop & State to show how `@Watch()` can be used on
// class members decorated with either `@Prop()` or `@State()`
import { Component, Prop, State, Watch } from "@stencil/core";

@Component({
  tag: "loading-indicator",
})
export class LoadingIndicator {
  // We decorate a class member with @Prop() so that we
  // can apply @Watch()
  @Prop() activated: boolean;
  // We decorate a class member with @State() so that we
  // can apply @Watch()
  @State() busy: boolean;

  // Apply @Watch() for the component's `activated` member.
  // Whenever `activated` changes, this method will fire.
  @Watch("activated")
  watchPropHandler(newValue: boolean, oldValue: boolean) {
    console.log("The old value of activated is: ", oldValue);
    console.log("The new value of activated is: ", newValue);
  }

  // Apply @Watch() for the component's `busy` member.
  // Whenever `busy` changes, this method will fire.
  @Watch("busy")
  watchStateHandler(newValue: boolean, oldValue: boolean) {
    console.log("The old value of busy is: ", oldValue);
    console.log("The new value of busy is: ", newValue);
  }

  @Watch("activated")
  @Watch("busy")
  watchMultiple(newValue: boolean, oldValue: boolean, propName: string) {
    console.log(`The new value of ${propName} is: `, newValue);
  }
}

在上面的例子中,有两个 @Watch() 装饰器。一个修饰了 watchPropHandler,当类成员 activated 发生变化时,这个修饰就会被触发。 另一个修饰了 watchStateHandler,它将在类成员 busy 发生变化时触发。

当触发时,@Watch() 的方法将接收 prop/state 的新旧值。这对于验证或处理副作用很有用。

提示

@Watch() 装饰器不会在组件初始加载时触发。

处理数组和对象

当 Stencil 检查使用 @Prop()@State() 修饰的类成员是否发生变化时,它会检查对类成员的引用是否发生了变化。 当一个类成员是一个对象或数组,并且被 @Prop()@State 标记时,对现有实体内容的改变不会导致 @Watch() 被触发,因为它不会改变对类成员的引用。

更新数组

对于数组,标准的可变数组操作,如 push()unshift() 不会触发组件更新。这些函数会改变数组的内容,但不会改变对数组本身的引用。

为了修改数组,应该使用非可变数组运算符。不可变数组操作符返回一个新数组的副本,可以高效地检测。 这些包括 map()filter(),以及展开运算符语法. 由 map()filter() 等返回的值应该被分配给正在被 @Prop()@State() 修饰的类成员。

例如,要将一个新元素推送到数组中,可以使用已有的值创建一个新数组,并在末尾添加新值:

tsx
import { Component, State, Watch, h } from "@stencil/core";

@Component({
  tag: "rand-numbers",
})
export class RandomNumbers {
  // We decorate a class member with @State() so that we
  // can apply @Watch(). This will hold a list of randomly
  // generated numbers
  @State() randNumbers: number[] = [];

  private timer: NodeJS.Timer;

  // Apply @Watch() for the component's `randNumbers` member.
  // Whenever `randNumbers` changes, this method will fire.
  @Watch("randNumbers")
  watchStateHandler(newValue: number[], oldValue: number[]) {
    console.log("The old value of randNumbers is: ", oldValue);
    console.log("The new value of randNumbers is: ", newValue);
  }

  connectedCallback() {
    this.timer = setInterval(() => {
      // generate a random whole number
      const newVal = Math.ceil(Math.random() * 100);

      /**
       * This does not create a new array. When stencil
       * attempts to see if any Watched members have changed,
       * it sees the reference to its `randNumbers` State is
       * the same, and will not trigger `@Watch` or a re-render
       */
      // this.randNumbers.push(newVal)

      /**
       * Using the spread operator, on the other hand, does
       * create a new array. `randNumbers` is reassigned
       * using the value returned by the spread operator.
       * The reference to `randNumbers` has changed, which
       * will trigger `@Watch` and a re-render
       */
      this.randNumbers = [...this.randNumbers, newVal];
    }, 1000);
  }

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

  render() {
    return (
      <div>
        randNumbers contains:
        <ol>
          {this.randNumbers.map((num) => (
            <li>{num}</li>
          ))}
        </ol>
      </div>
    );
  }
}

更新对象

应该使用展开运算符来更新对象。 与数组一样,在 Stencil 中修改对象不会触发视图更新。 但是,使用展开运算符并将其返回值赋值给正在被 @Prop()@State() 修饰的类成员将会触发视图更新。 下面是一个例子:

tsx
import { Component, State, Watch, h } from "@stencil/core";

export type NumberContainer = {
  val: number;
};

@Component({
  tag: "rand-numbers",
})
export class RandomNumbers {
  // We decorate a class member with @State() so that we
  // can apply @Watch(). This will hold a randomly generated
  // number.
  @State() numberContainer: NumberContainer = { val: 0 };

  private timer: NodeJS.Timer;

  // Apply @Watch() for the component's `numberContainer` member.
  // Whenever `numberContainer` changes, this method will fire.
  @Watch("numberContainer")
  watchStateHandler(newValue: NumberContainer, oldValue: NumberContainer) {
    console.log("The old value of numberContainer is: ", oldValue);
    console.log("The new value of numberContainer is: ", newValue);
  }

  connectedCallback() {
    this.timer = setInterval(() => {
      // generate a random whole number
      const newVal = Math.ceil(Math.random() * 100);

      /**
       * This does not create a new object. When stencil
       * attempts to see if any Watched members have changed,
       * it sees the reference to its `numberContainer` State is
       * the same, and will not trigger `@Watch` or are-render
       */
      // this.numberContainer.val = newVal;

      /**
       * Using the spread operator, on the other hand, does
       * create a new object. `numberContainer` is reassigned
       * using the value returned by the spread operator.
       * The reference to `numberContainer` has changed, which
       * will trigger `@Watch` and a re-render
       */
      this.numberContainer = { ...this.numberContainer, val: newVal };
    }, 1000);
  }

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

  render() {
    return <div>numberContainer contains: {this.numberContainer.val}</div>;
  }
}

此文档为非官方翻译版本