Properties
Props 是在 HTML 元素上公开的自定义 attributes/properties。它们允许开发人员将数据传递给组件来渲染或以其他方式使用。
Prop 装饰器
Props 使用 Stencil 的@Prop()
装饰器在组件上声明,如下所示:
// 首先, 从 '@stencil/core' 导入 Prop
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list",
})
export class TodoList {
// 其次, 我们使用 @Prop() 装饰一个类成员
@Prop() name: string;
render() {
// 在组件的类中,
// 它的 props 可以通过 `this` 来访问。
// 这允许我们渲染传递给 `todo-list` 的值。
return <div>To-Do List Name: {this.name}</div>;
}
}
在上面的例子中,@Prop()
放在(装饰) name
类成员之前,它是一个字符串。 通过给 name
添加 @Prop()
, Stencil 将把 name
暴露为元素上的一个属性,这个属性可以在组件使用的任何地方设置:
{
/* 在 TSX 文件中使用组件 */
}
<todo-list name={"Tuesday's To-Do List"}></todo-list>;
<!-- 在 HTML 文件中使用组件 -->
<todo-list name="Tuesday's To-Do List"></todo-list>
在上面的例子中,todo-list
组件在 TSX 和 HTML 中的使用几乎相同。两者之间的唯一区别是在 TSX 中,赋值给 prop(在这个例子中是 name
)的值被包裹在大括号中。 然而,在某些情况下,HTML 和 TSX 将属性传递给组件的方式略有不同。
Variable Casing
在 JavaScript 生态系统中,通常使用 'camelCase' 来命名变量。下面的示例组件有一个驼峰格式的类成员 thingToDo
。
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
// thingToDo is 'camelCased'
@Prop() thingToDo: string;
render() {
return <div>{this.thingToDo}</div>;
}
}
由于 thingToDo
是一个属性,我们可以在使用 todo-list-item
组件时为它提供一个值。 在 TSX 和 HTML 中,为驼峰式大小写属性(如thingToDo
)提供值几乎是相同的。
当我们在 TSX 文件中使用我们的组件时,一个属性使用 camelCase:
<todo-list-item thingToDo={"Learn about Stencil Props"}></todo-list-item>
在 HTML 中,属性必须使用 'dash-case',如下所示:
<todo-list-item thing-to-do="Learn about Stencil Props"></todo-list-item>
数据流
应该使用 Props 将数据从父组件传递给它的子组件。
下面的例子展示了一个 todo-list
组件如何使用三个 todo-list-item
子组件来渲染一个待办事项列表。
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list",
})
export class TodoList {
render() {
return (
<div>
<h1>To-Do List Name: Stencil To Do List</h1>
<ul>
{/* 下面是三个 Stencil 组件,它们是 `todo-list` 的子组件,每个组件代表我们列表中的一个项目 */}
<todo-list-item
thingToDo={"Learn about Stencil Props"}
></todo-list-item>
<todo-list-item
thingToDo={"Write some Stencil Code with Props"}
></todo-list-item>
<todo-list-item thingToDo={"Dance Party"}></todo-list-item>
</ul>
</div>
);
}
}
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() thingToDo: string;
render() {
return <li>{this.thingToDo}</li>;
}
}
提示
子组件不应该知道或引用它们的父组件。这允许 Stencil 高效地重新渲染组件。将组件的引用作为属性传递可能会导致意想不到的副作用。
可变性
默认情况下,Prop 在组件逻辑中是不可变的。一旦用户设置了值,组件就不能在内部更新它。 有关属性可变性的更高级控制,请参阅本文的mutable option部分。
类型
Props 可以是boolean
、number
、string
,甚至是Object
或Array
。下面的例子展示了todo-list-item
来添加更多不同类型的 props。
import { Component, Prop, h } from "@stencil/core";
// 在这个例子中,`MyHttpService` 是一个 `Object`
import { MyHttpService } from "../some/local/directory/MyHttpService";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() isComplete: boolean;
@Prop() timesCompletedInPast: number;
@Prop() thingToDo: string;
@Prop() myHttpService: MyHttpService;
}
布尔类型 prop
Stencil 组件的 prop 可以被声明为 boolean
类型:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() isComplete: boolean;
}
要在 HTML 中使用这个版本的 todo-list-item
,我们将字符串 "true"
/"false"
传递给组件:
<!-- 设置 isComplete 为 'true' -->
<todo-list-item is-complete="true"></todo-list-item>
<!-- 设置 isComplete 为 'false' -->
<todo-list-item is-complete="false"></todo-list-item>
要在 TSX 中使用这个版本的 todo-list-item
,需要使用 true
/false
,并用花括号括起来:
// 设置 isComplete 为 'true'
<todo-list-item isComplete={true}></todo-list-item>
// 设置 isComplete 为 'false'
<todo-list-item isComplete={false}></todo-list-item>
Stencil 处理boolean
类型属性的几种方式值得注意:
- 如果在 HTML 中提供字符串
"false"
,布尔属性的值将为false
<!-- 'todo-list-item' 组件的 isComplete 值为 `false` -->
<todo-list-item is-complete="false"></todo-list-item>
- 如果在 HTML 中提供的字符串不是
"false"
,布尔属性的值将为true
<!-- The 'todo-list-item' component will have an isComplete value of -->
<!-- `true` for each of the following examples -->
<todo-list-item is-complete=""></todo-list-item>
<todo-list-item is-complete="0"></todo-list-item>
<todo-list-item is-complete="False"></todo-list-item>
- 如果布尔属性没有默认值,并且满足以下条件之一,那么它的值将是
undefined
:- 在使用组件时,不包含 prop
- 在使用组件时包含了 prop,但没有赋值
<!-- Both examples using the 'todo-list-item' component will have an -->
<!-- isComplete value of `undefined` -->
<todo-list-item></todo-list-item>
<todo-list-item is-complete></todo-list-item>
数字类型 prop
Stencil 组件的 prop 可以被声明为 number
类型:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() timesCompletedInPast: number;
}
要在 HTML 中使用这个版本的 todo-list-item
,我们将数值作为字符串传递给组件:
<!-- 设置 timesCompletedInPast 为 '0' -->
<todo-list-item times-completed-in-past="0"></todo-list-item>
<!-- 设置 timesCompletedInPast 为 '23' -->
<todo-list-item times-completed-in-past="23"></todo-list-item>
要在 TSX 中使用这个版本的 todo-list-item
,需要将一个由大括号括起来的数字传递给组件:
// 设置 timesCompletedInPast 为 '0'
<todo-list-item timesCompletedInPast={0}></todo-list-item>
// 设置 timesCompletedInPast 为 '23'
<todo-list-item timesCompletedInPast={23}></todo-list-item>
字符串类型 Prop
Stencil 组件的 prop 可以被声明为 string
类型:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() thingToDo: string;
}
要在 HTML 中使用这个版本的 todo-list-item
,我们将值作为字符串传递给组件:
<!-- 设置 thingToDo 为 'Learn about Stencil Props' -->
<todo-list-item thing-to-do="Learn about Stencil Props"></todo-list-item>
<!-- 设置 thingToDo 为 'Write some Stencil Code with Props' -->
<todo-list-item
thing-to-do="Write some Stencil Code with Props"
></todo-list-item>
要在 TSX 中使用这个版本的 todo-list-item
,我们将值作为字符串传递给组件。在 TSX 中向 props 提供字符串值时,大括号不是必需的:
// 设置 thingToDo 为 'Learn about Stencil Props'
<todo-list-item thingToDo="Learn about Stencil Props"></todo-list-item>
// 设置 thingToDo 为 'Write some Stencil Code with Props'
<todo-list-item thingToDo="Write some Stencil Code with Props"></todo-list-item>
// 使用大括号蒋 thingToDo 设置为 'Write some Stencil Code with Props'
<todo-list-item thingToDo={"Learn about Stencil Props"}></todo-list-item>
Object Props
Stencil 组件的 prop 可以被声明为 Object
类型:
// TodoListItem.tsx
import { Component, Prop, h } from "@stencil/core";
import { MyHttpService } from "../path/to/MyHttpService";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
// Use `@Prop()` to declare the `httpService` class member
@Prop() httpService: MyHttpService;
}
// MyHttpService.ts
export class MyHttpService {
// This implementation intentionally left blank
}
在 TypeScript 中,MyHttpService
既是一个 对象
也是一个 类型
。 当使用像 MyHttpService
这样的用户定义类型时,类型必须始终在声明时使用 export
关键字导出。 这样做的原因是当从父组件传递MyHttpService
的实例给TodoListItem
时,Stencil 需要知道httpService
是什么类型的 prop。
要在 TSX 中设置 httpService
,请将自定义元素标签中的属性名称分配给所需的值,如下所示:
// TodoList.tsx
import { Component, h } from "@stencil/core";
import { MyHttpService } from "../MyHttpService";
@Component({
tag: "todo-list",
styleUrl: "todo-list.css",
shadow: true,
})
export class ToDoList {
private httpService = new MyHttpService();
render() {
return <todo-list-item httpService={this.httpService}></todo-list-item>;
}
}
注意,属性名使用了 camelCase
,值被大括号括起来。
不能像下面这样通过 HTML 属性来设置 Object
类型的 prop:
<!-- this will not work -->
<todo-list-item
http-service="{ /* implementation omitted */ }"
></todo-list-item>
这样做的原因是,Stencil 不会尝试将 HTML 中类似对象的字符串序列化为 JavaScript 对象。 类似地,Stencil 不支持从 JSON 反序列化对象。 执行这两种操作在运行时都很昂贵,并且有丢失对其他嵌套 JavaScript 对象的引用的风险。
相反,可以通过 HTML 中的 <script>
标签设置属性:
<script>
document.querySelector("todo-list-item").httpService = {
/* implementation omitted */
};
</script>
Array Props
Stencil 组件的 prop 可以被声明为 Array
类型:
// TodoList.tsx
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() itemLabels: string[];
}
要在 TSX 中设置 itemLabels
,需要在自定义元素的标签中指定所需的值,如下所示:
// TodoList.tsx
import { Component, h } from "@stencil/core";
import { MyHttpService } from "../MyHttpService";
@Component({
tag: "todo-list",
styleUrl: "todo-list.css",
shadow: true,
})
export class ToDoList {
private labels = ["non-urgent", "weekend-only"];
render() {
return <todo-list-item itemLabels={this.labels}></todo-list-item>;
}
}
注意,属性名使用了 camelCase
,值被大括号括起来。
不能通过 HTML 属性来设置 Array
类型的 prop:
<!-- this will not work -->
<todo-list-item item-labels="['non-urgent', 'weekend-only']"></todo-list-item>
这样做的原因是,Stencil 不会尝试将 HTML 中类似数组的字符串序列化为 JavaScript 对象。 这样做在运行时开销很大,并且有丢失对其他嵌套 JavaScript 对象的引用的风险。
相反,可以通过 HTML 中的 <script>
标签设置属性:
<script>
document.querySelector("todo-list-item").itemLabels = [
"non-urgent",
"weekend-only",
];
</script>
高级的 Prop 类型
any
类型
TypeScript 的' any '类型是一种特殊类型,可用于防止对特定值进行类型检查。 因为any
是 TypeScript 中的有效类型,所以 Stencil props 也可以被指定为 any
类型。下面的例子演示了三种使用类型为 any
的 props 的不同方式:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
// isComplete有一个显式的类型注解 `any`,
// 并且没有默认值
@Prop() isComplete: any;
// label has an explicit type annotation of
// `any` with a default value of 'urgent',
// which is a string
@Prop() label: any = "urgent";
// thingToDo has no type and no default value,
// and will be considered to be type `any` by
// TypeScript
@Prop() thingToDo;
render() {
return (
<ul>
<li>
isComplete has a value of - {this.isComplete} - and a typeof value of
"{typeof this.isComplete}"
</li>
<li>
label has a value of - {this.label} - and a typeof value of "
{typeof this.label}"
</li>
<li>
thingToDo has a value of - {this.thingToDo} - and a typeof value of "
{typeof this.thingToDo}"
</li>
</ul>
);
}
}
当使用类型为 any
的模板属性时(隐式或显式),提供给属性的值保留自己的类型信息。Stencil 和 TypeScript 都不会尝试改变属性的类型。 为了演示,让我们使用两次 todo-list-item
,每次使用不同的 prop 值:
{/* Using todo-list-item in TSX using differnt values each time */}
<todo-list-item isComplete={42} label={null} thingToDo={"Learn about any-typed props"}></todo-list-item>
<todo-list-item isComplete={"42"} label={1} thingToDo={"Learn about any-typed props"}></todo-list-item>
以下内容将根据上面的用法示例渲染:
- isComplete has a value of - 42 - and a typeof value of "number"
- label has a value of - - and a typeof value of "object"
- thingToDo has a value of - Learn about any-typed props - and a typeof value of "string"
- isComplete has a value of - 42 - and a typeof value of "string"
- label has a value of - 1 - and a typeof value of "number"
- thingToDo has a value of - Learn about any-typed props - and a typeof value of "string"
在 todo-list-item
的第一次使用中,给 isComplete
提供了一个 42 的数值,而在第二次使用中,它接收了一个包含"42"的字符串。 isComplete
上的类型分别反映了它提供的值的类型 number
和 string
。
看看label
,值得注意的是,尽管属性有一个默认值,但它并没有将 label
的类型缩小为 string
类型。 在 todo-list-item
的第一次使用中,label
的值是 null,而在第二次使用中,它的值是 1。存储在 label
中的值的类型分别正确地报告为 object
和 number
。
可选类型
TypeScript 允许通过在成员名称的末尾附加 "?" 将成员标记为可选。下面的示例演示了使每个组件的道具都是可选的:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
// completeMsg 是可选的,有一个显式的
// `string` 类型注解,并且没有默认值
@Prop() completeMsg?: string;
// label 是可选的,没有显式类型注释
// 但有默认值 'urgent'
@Prop() label? = "urgent";
// thingToDo 没有类型注释也没有默认值
@Prop() thingToDo?;
render() {
return (
<ul>
<li>
completeMsg has a value of - {this.completeMsg} - and a typeof value
of "{typeof this.completeMsg}"
</li>
<li>
label has a value of - {this.label} - and a typeof value of "
{typeof this.label}"
</li>
<li>
thingToDo has a value of - {this.thingToDo} - and a typeof value of "
{typeof this.thingToDo}"
</li>
</ul>
);
}
}
当使用标记为可选的 Stencil prop 时,如果没有明确指定类型,Stencil 将尝试推断 prop 的类型。在上面的例子中,Stencil 能够理解:
因为 Stencil 可以推断出 label
的类型,下面的代码会因为类型不匹配而编译失败:
{
/* 这将导致编译失败,并出现标签 prop 的错误 "Type 'number' is not assignable to type 'string'"。 */
}
<todo-list-item
completeMsg={"true"}
label={42}
thingToDo={"Learn about any-typed props"}
></todo-list-item>;
值得注意的是,当在 HTML 文件中使用组件时,这种类型检查是不可用的。这是 HTML 的一个约束,提供给属性的所有值的类型都是 string:
<!-- 在 html 中使用 todo-list-item -->
<todo-list-item
complete-msg="42"
label="null"
thing-to-do="Learn about any-typed props"
></todo-list-item>
渲染成:
- completeMsg has a value of - 42 - and a typeof value of "string"
- label has a value of - null - and a typeof value of "string"
- thingToDo has a value of - Learn about any-typed props - and a typeof value of "string"
Union Types
Stencil 允许 props 类型为 union types, 这允许开发人员将两个或多个现有类型组合起来创建一个新的类型。 下面的例子展示了一个 todo-list-item
对象,它接受一个 isComplete
属性,该属性可以是字符串或布尔值。
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() isComplete: string | boolean;
}
这个组件可以在 HTML 中使用:
<todo-list-item is-complete="true"></todo-list-item>
<todo-list-item is-complete="false"></todo-list-item>
和 TSX:
<todo-list-item isComplete={true}></todo-list-item>
<todo-list-item isComplete={false}></todo-list-item>
默认值
Stencil props 可以被赋予一个默认值,作为未提供 prop 的回退:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "component-with-some-props",
})
export class ComponentWithSomeProps {
@Prop() aNumber = 42;
@Prop() aString = "defaultValue";
render() {
return (
<div>
The number is {this.aNumber} and the string is {this.aString}
</div>
);
}
}
无论我们是在 HTML 还是 TSX 中使用这个组件,当没有值传递给我们的组件时,都会显示 "number is 42 and The string is defaultValue":
<component-with-some-props></component-with-some-props>
可以通过为带有默认值的 prop 指定值来覆盖组件上的默认值。 对于下面的例子, 将渲染成 "The number is 7 and the string is defaultValue"。 注意,提供给 aNumber
的值覆盖了默认值,但 aString
的默认值仍然保持不变。
<component-with-some-props a-number="7"></component-with-some-props>
从默认值推断类型
当提供默认值时,Stencil 能够从默认值推断出 prop 的类型:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "component-with-many-props",
})
export class ComponentWithManyProps {
// 下面两个 prop 的类型都是 'boolean'
@Prop() boolean1: boolean;
@Prop() boolean2 = true;
// 下面两个 prop 的类型都是 'number'
@Prop() number1: number;
@Prop() number2 = 42;
// 下面两个 prop 的类型都是 'string'
@Prop() string1: string;
@Prop() string2 = "defaultValue";
}
必填的 prop
通过在 prop 名称之后附加 !
,Stencil 根据需要标记该 attribute/property 为必填。这样可以确保在 TSX 中使用组件时,将使用该属性:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
// Note the '!' after the variable name.
@Prop() thingToDo!: string;
}
Prop 校验
要校验 Prop, 可以使用 @Watch() 装饰器:
import { Component, Prop, Watch, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class TodoList {
// Mark the prop as required, to make sure it is provided when we use `todo-list-item`.
// We want stricter guarantees around the contents of the string, so we'll use `@Watch` to perform additional validation.
@Prop() thingToDo!: string;
@Watch("thingToDo")
validateName(newValue: string, _oldValue: string) {
// don't allow `thingToDo` to be the empty string
const isBlank = typeof newValue !== "string" || newValue === "";
if (isBlank) {
throw new Error("thingToDo is a required property and cannot be empty");
}
// don't allow `thingToDo` to be a string with a length of 1
const has2chars = typeof newValue === "string" && newValue.length >= 2;
if (!has2chars) {
throw new Error("thingToDo must have a length of more than 1 character");
}
}
}
@Prop() Options
@Prop()
装饰器接受一个可选参数来指定特定的选项,以修改组件上 prop 的行为。@Prop()
的可选参数是一个对象字面量,包含以下一个或多个字段:
export interface PropOptions {
attribute?: string;
mutable?: boolean;
reflect?: boolean;
}
Attribute Name (attribute
)
Properties 和组件 attributes 是强关联的,但不一定是同一件事。attributes 是一个 HTML 概念,但 properties 是 JavaScript 中面向对象编程固有的概念。
在 Stencil 中,应用于 property 的 @Prop()
装饰器将指示 Stencil 编译器也监听 DOM 属性的变化。
通常,property 名与 attribute 名相同,但情况并不总是如此。以下面的组件为例:
import { Component, Prop, h } from "@stencil/core";
// `MyHttpService` is an `Object` in this example
import { MyHttpService } from "../some/local/directory/MyHttpService";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop() isComplete: boolean;
@Prop() thingToDo: string;
@Prop() httpService: MyHttpService;
}
这个组件有 3 properties, 但是编译器只会创建 2 attributes: is-complete
和 thing-to-do
.
<todo-list-item is-complete="false" thing-to-do="Read Attribute Naming Section of Stencil Docs"></my-cmp>
请注意,httpService
类型不是原始类型(例如,不是 number
、boolean
或 string
)。由于 DOM 属性只能是字符串,因此使用 "http-service"
作为 DOM 属性是没有意义的。Stencil 不会尝试将 HTML 中类似对象的字符串序列化为 JavaScript 对象。 关于如何配置 httpService
,请参阅 Object Props。
同时,isComplete
和 thingToDo
properties 遵循 camelCase
命名,但 attributes 不区分大小写,因此 attributes 名称默认为 is-complete
和 thing-to-do
。
幸运的是,这种“默认”行为可以使用 @Prop()
装饰器的 attribute
选项来改变:
import { Component, Prop, h } from "@stencil/core";
// `MyHttpService` is an `Object` in this example
import { MyHttpService } from "../some/local/directory/MyHttpService";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop({ attribute: "complete" }) isComplete: boolean;
@Prop({ attribute: "thing" }) thingToDo: string;
@Prop({ attribute: "my-service" }) httpService: MyHttpService;
}
通过使用这个选项,当在 HTML 中使用组件时,我们可以明确地知道哪些 property 有关联的 DOM attribute 以及 attribute 的名称。
<todo-list-item complete="false" thing="Read Attribute Naming Section of Stencil Docs" my-service="{}"></my-cmp>
Prop Mutability (mutable
)
默认情况下,Prop 在组件内部是不可变的。然而,通过将 Prop 声明为可变的,可以显式地允许在组件内部改变 Prop,如下面的例子所示:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop({ mutable: true }) thingToDo: string;
componentDidLoad() {
this.thingToDo = "Ah! A new value!";
}
}
Mutable Arrays and Objects
Stencil 通过引用来比较 Props,以便高效地重新渲染组件。 在一个对象或数组的 Prop 上设置 mutable: true
允许组件内部对该 Prop 的引用发生改变并触发渲染。 它不允许通过对现有对象或数组的可变更改来触发渲染。
例如,要更新数组 Prop:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "my-component",
})
export class MyComponent {
@Prop({ mutable: true }) contents: string[] = [];
timer: NodeJS.Timer;
connectedCallback() {
this.timer = setTimeout(() => {
// this does not create a new array. when stencil
// attempts to see if any of its Props have changed,
// it sees the reference to its `contents` Prop is
// the same, and will not trigger a render
// this.contents.push('Stencil')
// this does create a new array, and therefore a
// new reference to the Prop. Stencil will pick up
// this change and rerender
this.contents = [...this.contents, "Stencil"];
// after 3 seconds, the component will re-render due
// to the reference change in `this.contents`
}, 3000);
}
disconnectedCallback() {
if (this.timer) {
clearTimeout(this.timer);
}
}
render() {
return <div>Hello, World! I'm {this.contents[0]}</div>;
}
}
在上面的例子中,使用 this.contents.push('Stencil')
原地更新 prop 是没有效果的。 Stencil 看不到对 this.contents
的更改。因为它查看了 prop 的引用并发现它没有改变。 这样做是出于性能考虑。 如果 Stencil 必须遍历数组中的每个值来确定它是否发生了变化,则会导致性能下降。 相反,重新为 prop 赋值(在上面的例子中,我们使用扩展运算符)被认为是更好的性能和更习惯的做法。
这同样适用于对象。 应该使用展开运算符创建一个新对象,而不是原地改变现有对象。这个对象会因引用而不同,因此会触发重新渲染:
import { Component, Prop, h } from "@stencil/core";
export type MyContents = { name: string };
@Component({
tag: "my-component",
})
export class MyComponent {
@Prop({ mutable: true }) contents: MyContents;
timer: NodeJS.Timer;
connectedCallback() {
this.timer = setTimeout(() => {
// this does not create a new object. when stencil
// attempts to see if any of its Props have changed,
// it sees the reference to its `contents` Prop is
// the same, and will not trigger a render
// this.contents.name = 'Stencil';
// this does create a new object, and therefore a
// new reference to the Prop. Stencil will pick up
// this change and rerender
this.contents = { ...this.contents, name: "Stencil" };
// after 3 seconds, the component will re-render due
// to the reference change in `this.contents`
}, 3000);
}
disconnectedCallback() {
if (this.timer) {
clearTimeout(this.timer);
}
}
render() {
return <div>Hello, World! I'm {this.contents.name}</div>;
}
}
Reflect Properties Values to Attributes (reflect
)
在某些情况下,让 prop 和 attribute 保持同步可能很有用。在这种情况下,你可以将 @Prop()
装饰器中的 reflect
选项设置为 true
。 当一个 prop 被反射时,它会作为一个 HTML 属性呈现在 DOM 中。
以下面的组件为例:
import { Component, Prop, h } from "@stencil/core";
@Component({
tag: "todo-list-item",
})
export class ToDoListItem {
@Prop({ reflect: false }) isComplete: boolean = false;
@Prop({ reflect: true }) timesCompletedInPast: number = 2;
@Prop({ reflect: true }) thingToDo: string =
"Read Reflect Section of Stencil Docs";
}
上面示例中的组件使用了默认值,可以像这样在 HTML 中使用:
<!-- Example of using todo-list-item in HTML -->
<todo-list-item></todo-list-item>
当在 DOM 中渲染时,配置了 reflect: true
的 prop 将作为 HTML 属性在 DOM 中反映:
<todo-list-item
times-completed-in-past="2"
thing-to-do="Read Reflect Section of Stencil Docs"
></todo-list-item>
虽然没有设置为 "reflect" 的属性,例如 isComplete
,没有被渲染为 DOM 属性,但这并不意味着它不存在——isComplete
属性仍然包含被赋值的 false
值:
const cmp = document.querySelector("todo-list-item");
console.log(cmp.isComplete); // it prints 'false'