字段、属性和特性

在组件的 JavaScript 类中声明字段。在组件的模板中引用它们以动态更新内容。

字段属性几乎是可以互换的术语。组件作者声明类中的字段。类的实例具有属性。对于组件使用者来说,字段是属性。在 Lightning Web 组件中,只有组件作者用来修饰的字段才能作为对象属性公开提供给使用者。@api

属性和属性几乎是可以互换的术语,可能会造成混淆。一般来说,在 HTML 中我们谈论属性,在 JavaScript 中我们谈论属性。

提示

如果您已经开发过 Aura 组件,那么您就熟悉术语 attribute。在 Lightning Web 组件中,最接近 Aura 属性的是 JavaScript 属性。

反应

反应性是 Lightning Web 组件框架的核心系统。该框架观察字段和属性值的更改。当它观察到变化时,它会做出反应。它重新计算模板中使用的所有表达式,并重新呈现组件,从而显示新值。

字段属性几乎是可以互换的术语。组件作者声明类中的字段。类的实例具有属性。对于组件使用者来说,字段是属性。在 Lightning Web 组件中,只有组件作者用来修饰的字段才能作为对象属性公开提供给使用者。@api

公共属性

若要公开公共属性,请使用 修饰字段。公共属性定义组件的 API。模板中使用的公共属性是反应式的。如果模板中使用的公共属性的值发生更改,则组件将重新呈现。@api

当组件重新渲染时,将重新计算模板中使用的表达式,并执行 renderedCallback() 生命周期钩子。

@api装饰器

https://youtube.com/watch?v=F7J5yn3MIXw

提示

装饰器是一种 JavaScript 语言功能。@api装饰器是 Lightning Web 组件独有的。一个字段只能有一个修饰器。

从 导入装饰器。此代码将字段公开为公共属性。@apilwcitemName

// todoItem.js
import { LightningElement, api } from "lwc";
export default class TodoItem extends LightningElement {
  @api itemName = "New Item";
}

在模板中显示的值。itemName

<!-- todoItem.html -->
<template>
  <div class="view">
    <label>{itemName}</label>
  </div>
</template>

此 JavaScript 类和 HTML 模板定义组件,其中是命名空间。使用该组件的组件可以设置该属性。c-todo-itemcc-todo-itemitemName

注意

JavaScript 中的属性名称采用驼峰大小写,而 HTML 属性名称采用烤肉串大小写(破折号分隔)以匹配 HTML 标准。在 中,标记中的属性映射到 的 JavaScript 属性。todoApp.htmlitem-nameitemNamec-todo-item

<!-- todoApp.html -->
<template>
  <div class="listing">
    <c-todo-item item-name="Milk"></c-todo-item>
    <c-todo-item item-name="Bread"></c-todo-item>
  </div>
</template>

在其标记中使用组件的所有者组件可以通过 DOM 属性访问组件的公共属性。DOM 属性是在类中声明的公共字段。它们可以通过带有点表示法的 DOM 元素访问。在此示例中,该组件将一个公共字段声明为 ,其值可通过 (DOM 元素)实例上的 DOM 属性访问。c-todo-item@api itemNamec-todo-item

// todoApp.js
myItem = this.template.querySelector("c-todo-item").itemName;

提示

请参阅存储库中的 CompositionBasics 示例。lwc-recipes

字段、对象和数组的反应性

如果字段的值发生更改,并且该字段在模板中使用或在模板中使用的属性的 getter 中使用,则组件将重新呈现并显示新值。如果为字段分配了对象或数组,则框架会观察到对象或数组内部的一些更改,例如在分配新值时。

当组件重新渲染时,将重新计算模板中使用的表达式,并执行 renderedCallback() 生命周期钩子。

当您在“名字”或“姓氏”字段中输入值时,组件会将其转换为大写并显示。

名字和姓氏的输入字段。输入的名称将转换为大写。
<!-- helloExpressions.html -->

<template>
  <lightning-card title="HelloExpressions" icon-name="custom:custom14">
    <div class="slds-m-around_medium">
      <lightning-input
        name="firstName"
        label="First Name"
        onchange={handleChange}
      ></lightning-input>
      <lightning-input
        name="lastName"
        label="Last Name"
        onchange={handleChange}
      ></lightning-input>
      <p class="slds-m-top_medium">Uppercased Full Name: {uppercasedFullName}</p>
    </div>
  </lightning-card>
</template>

组件的类定义了 和 字段。由于它们在模板 () 中使用的属性的 getter 中使用,因此当其值更改时,组件会重新呈现。firstNamelastNameuppercasedFullName

// helloExpressions.js

import { LightningElement } from "lwc";

export default class TrackExample extends LightningElement {
  firstName = "";
  lastName = "";

  handleChange(event) {
    const field = event.target.name;
    if (field === "firstName") {
      this.firstName = event.target.value;
    } else if (field === "lastName") {
      this.lastName = event.target.value;
    }
  }

  get uppercasedFullName() {
    return `${this.firstName} ${this.lastName}`.trim().toUpperCase();
  }
}

注意

字段是反应式的。Expando 属性是在运行时添加到对象的属性,不是响应式的。

提示

请参阅存储库中的 helloExpressions 组件。lwc-recipes

反应性注意事项

尽管字段是反应式的,但 LWC 引擎以浅层方式跟踪字段值更改。通过使用 比较值标识来将新值分配给字段时,将检测到更改。此方法适用于数字或布尔值等基元类型。===

import { LightningElement } from "lwc";

export default class ReactivityExample extends LightningElement {
  bool = true;
  number = 42;
  obj = { name: "John" };

  checkMutation() {
    this.bool = false; // Mutation detected

    this.number = 42; // No mutation detected: previous value is equal to the newly assigned value
    this.number = 43; // Mutation detected

    this.obj.name = "Bob"; // No mutation detect: `obj` field value is not reassigned
    this.obj = { name: "John" }; // Mutation detected - redefining the object with the same value creates a new object
    this.obj = { ...this.obj, title: "CEO" }; // Mutation detected
  }
}

在操作复杂类型(如对象和数组)时,必须创建一个新对象并将其分配给要检测更改的字段。

为了避免在处理复杂对象时出现此类问题,请使用修饰器深入跟踪对字段值所做的更改。@track

跟踪对象和数组内部的更改

若要观察对象属性或数组元素的更改,请使用 修饰字段。@track

当字段修饰有 时,Lightning Web 组件会跟踪对以下各项内部值的更改:@track

  • 使用以下方式创建的普通对象{}
  • 使用[]

该框架以递归方式观察对普通对象和数组的突变,包括嵌套对象、嵌套数组以及对象和数组的混合。还处理循环引用。

但是,该框架不会观察到对复杂对象(例如继承自 、类实例、 、 或 的对象)的突变。ObjectDateSetMap

@track装饰器

https://youtube.com/watch?v=evIUIHcWbZI

观察对象的属性

要告诉框架观察对象属性的变化,请使用 修饰字段。@track

注意

如前所述,在不使用 的情况下,框架会观察到为字段分配新值的更改。如果新值不是以前的值,则组件将重新呈现。@track===

例如,让我们稍微更改代码以声明字段,该字段包含一个具有两个属性的对象,以及 .该框架会观察到将新值分配给 的更改。fullNamefirstNamelastNamefullName

fullName = { firstName: "", lastName: "" };

此代码为字段分配一个新值,以便组件重新呈现。fullName

// Component rerenders.
this.fullName = { firstName: "John", lastName: "Doe" };

但是,如果我们为对象的某个属性分配一个新值,则组件不会重新呈现,因为不会观察到这些属性。

// Component doesn't rerender.
this.fullName.firstName = "John";

该框架会观察到为字段分配新值的更改。此代码不会执行此操作,而是为对象的属性分配一个新值。fullNamefirstNamefullName

要告诉框架观察对象属性的变化,请使用 修饰字段。现在,如果我们更改任一属性,组件就会重新渲染。fullName@track

// Component rerenders.
@track fullName = { firstName : '', lastName : ''};
this.fullName.firstName = 'John';

如果属性包含对象,若要跟踪对象属性的更改,请使用 注释该属性。为了理解,让我们修改我们的示例。我们的示例初始化 and 为空字符串,这些字符串是原始值,因此组件在它们更改时会重新呈现。@trackfirstNamelastName

使用新属性重新呈现对象

仅当更新了在上一个渲染周期中访问的属性时,组件才会重新渲染,即使对象使用 批注了 。这样可以防止组件过度重新渲染,@track

考虑这个跟踪对象和一个打印对象属性的 getter。

@track obj = {value1: 'Hello'};

get words() {
    return Object.entries(this.obj)
              .map(([key, value]) => ({key, value}));
  }

在第一个渲染周期中,框架会记录所访问的内容。任何不影响的突变都会被忽略,因为它不会影响呈现的内容。因此,更改 会触发重新呈现,但向 添加新属性或更改不会触发重新呈现。obj.value1objvalue1value1objvalue2

// Component rerenders.
setValue1(e) {
  this.obj.value1 = 'Hello World';
}

// Component doesn’t rerender.
setValue2(e) {
  this.obj.value2 = 'Hello LWC';
}

若要在添加新属性时重新呈现组件,请将该对象分配给具有这两个值的新对象。

setValue2(e) {
  this.obj = {
    ...this.obj,
      value2: 'Hello LWC'
  };
}

观察数组的元素

另一个用例是告诉框架观察数组元素的变化。@track

如果不使用 ,框架会观察到为字段分配新值的更改。@track

arr = ["a", "b"];

当您将新值分配给 时,组件将重新呈现。arr

// Component rerenders.
this.arr = ["x", "y", "z"];

但是,如果我们在数组中更新或添加元素,则该组件不会重新渲染。

// Component doesn’t rerender.
this.arr[0] = "x";
this.arr.push("c");

若要告诉框架观察数组元素的变化,请使用 此外,框架不会自动将数组转换为字符串以更新数组元素。要返回更新的字符串,请使用 getter 将数组的元素转换为字符串。arr@trackjoin()

@track arr = ['a','b'];

get computedArray() { return this.arr.join(','); }

update() {
  this.arr[0] = 'x';
  this.arr.push('c');
}

观察复杂物体

让我们看一个字段为 的组件,其类型为 。该模板有几个按钮,用于更改 的内部状态。此示例突出显示了创建对象,因为它不是普通的 JavaScript 对象,LWC 引擎不会观察到内部状态突变,即使代码使用 .xDatexnew Date()@track

// trackDate.js
import { LightningElement, track } from "lwc";
export default class TrackDate extends LightningElement {
  @track x = new Date();

  initDate() {
    this.x = new Date();
  }

  updateDate() {
    this.x.setHours(7); // No mutation detected
  }
}

与前面的示例类似,该模板有几个按钮可以更改 的内部状态。x

<!-- trackDate.html -->
<template>
  <p>Date: {x}</p>

  <button onclick={initDate}>Init</button>
  <button onclick={updateDate}>Update</button>
</template>

单击“初始化”按钮时,将观察到更改并重新呈现模板。Lightning Web Components 观察到它指向一个新对象。但是,单击“更新”时,不会重新呈现模板。Lightning Web 组件不会观察到对象值的变化。xDateDate

若要确保在值更改时重新呈现模板,请克隆现有日期并更新其值。

updateDate() {
  const cloned = new Date(this.x.getTime());
  cloned.setHours(7);

  // Assign the new date instance to rerender the component.
  this.x = cloned;
}

注意

将属性设置为无法跟踪的值时,将记录警告。如果尝试调试组件在更改时未重新呈现的方案,请查看浏览器控制台。在我们的示例中,浏览器控制台会记录以下有用的警告:

Property "x" of [object:vm TrackDate] is set to a non-trackable object, which means changes into that object cannot be observed.

属性和属性名称

JavaScript 中的属性名称采用驼峰大小写,而 HTML 属性名称采用烤肉串大小写(破折号分隔)以匹配 HTML 标准。

例如,名为 的 JavaScript 属性映射到名为 的 HTML 属性。itemNameitem-name

// child.js
import { LightningElement, api } from "lwc";

export default class extends LightningElement {
  @api itemName;
}
<!-- parent.html -->
<template>
  <c-child item-name="Child item"></c-child>
</template>

JavaScript 属性名称

不要以这些字符开头属性名称。

  • on(例如,onClick)
  • aria(例如,ariaDescribedby)
  • data(例如,dataProperty)

不要将这些保留字用于属性名称。

  • slot
  • part
  • is

HTML 属性名称

模板中的 HTML 属性不能包含大写字符。属性名称可以以以下字母开头:

  • 小写字母字符
  • 下划线 (_)
  • 美元符号 ($)
  • 可选连字符后跟字母字符(-a)

属性名称可以包含重复的下划线 () 或下划线后跟连字符 ()。___-

如果连字符不是属性名称中的第一个字符,则允许使用连字符后跟下划线 ()。例如,这些是有效的属性名称:-_

  • _myattribute
  • $myattribute
  • my_-attribute

如果 JavaScript 属性以大写字符开头,并且希望通过 HTML 属性进行设置,则必须使用特殊语法。属性名称的大写字符为小写,并以连字符 为前缀。前导连字符告诉引擎,属性名称中的第一个字母字符在 JavaScript 类中是用前导大写字符声明的。例如,JavaScript 属性对应于 HTML 属性。-upper@api Upperupper

在 JavaScript 中访问 HTML 全局属性

不建议使用 HTML 全局属性,这些属性类似于所有 HTML 元素,并且是所有 HTML 元素通用的属性。如果确实使用全局 HTML 属性,请使用 .classtitle@api

某些 HTML 全局属性不遵循 Lightning Web 组件驼峰大小写和烤肉串大小写约定。如果在 JavaScript 中为这些 HTML 全局属性之一创建 getter 或 setter,请使用此列表中的大小写。

HTML 全局属性JavaScript 中的属性
accesskeyaccessKey
bgcolorbgColor
colspancolSpan
contenteditablecontentEditable
crossorigincrossOrigin
datetimedateTime
forhtmlFor
formactionformAction
ismapisMap
maxlengthmaxLength
minlengthminLength
novalidatenoValidate
readonlyreadOnly
rowspanrowSpan
tabindextabIndex
usemapuseMap

例如,要访问 JavaScript 中的 HTML 属性,请使用 .maxlengthtextareamaxLength

此外,要从 JavaScript 访问 HTML 属性,请使用 .forhtmlFor

在 JavaScript 中访问 ARIA 属性

ARIA 属性支持辅助技术。要在 JavaScript 中访问这些属性,请使用驼峰大小写。例如,要访问 ,请使用 .要访问 ,请使用 .aria-checkedariaCheckedaria-errormessageariaErrorMessage

Web API 属性

Lightning Web 组件反映了许多 Web API 的属性。

元素

Lightning Web 组件反映了 Element 界面的这些属性。

childrenclassListclassNamefirstElementChildgetAttributegetAttributeNSgetBoundingClientRectgetElementsByClassNamegetElementsByTagNamehasAttributeidlastElementChildquerySelectorquerySelectorAllremoveAttributeremoveAttributeNSsetAttributeNSsetAttributeshadowRootslot

请参阅 Shadow DOM、访问组件拥有的元素以及将标记传递到插槽中。

在 Salesforce 组织中启用 Lightning Web Security 后,setAttributeNS、setAttribute 和 shadowRoot 会因失真而修改。

事件目标

Lightning Web 组件反映了 EventTarget 接口的这些属性。

addEventListenerdispatchEventremoveEventListener

请参阅与事件通信。

HTMLElement

Lightning Web 组件反映了 HTMLElement 接口的这些属性。

accessKeyLabelcontentEditabledatasetdirhiddenisContentEditablelangoffsetHeightoffsetLeftoffsetParentoffsetTopoffsetWidthtitle

在 Salesforce 组织中启用 Lightning Web Security 时,数据集会因失真而修改。

节点

Lightning Web 组件反映了 Node 界面的这些属性。

childNodesfirstChildisConnectedlastChild

请参见在 DOM 中插入或删除组件时运行代码。

WAI-ARIA 状态和属性

Lightning Web 组件反映了这些 WAI-ARIA 状态和属性。

ariaActiveDescendantariaAtomicariaAutoCompleteariaBusyariaCheckedariaColCountariaColIndexariaColSpanariaControlsariaCurrentariaDescribedByariaDetailsariaDisabledariaErrorMessageariaExpandedariaFlowToariaHasPopupariaHiddenariaInvalidariaKeyShortcutsariaLabelariaLabelledByariaLevelariaLiveariaModalariaMultiLineariaMultiSelectableariaOrientationariaOwnsariaPlaceholderariaPosInSetariaPressedariaReadOnlyariaRelevantariaRequiredariaRoleDescriptionariaRowCountariaRowIndexariaRowSpanariaSelectedariaSetSizeariaSortariaValueMaxariaValueMinariaValueNowariaValueText

请参阅组件可访问性。

使用 getter 和 setter 修改数据

若要在每次设置公共属性时执行逻辑,请编写自定义 setter。

如果为公共属性编写 setter,则还必须编写 getter。用 注释 getter 或 setter,但不能同时注释两者。最佳做法是注释 getter。@api

若要将属性值保存在 getter 和 setter 中,请使用字段。此示例使用该属性,该属性以下划线为前缀,以指示该属性是私有的。_uppercaseItemName

此示例组件将字符串转换为大写。<c-todo-item>

<!-- todoItem.html -->
<template> {itemName} </template>

属性值通过 getter 提供给模板。

// todoItem.js
import { LightningElement, api } from "lwc";
export default class TodoItem extends LightningElement {
  _uppercaseItemName;

  @api
  get itemName() {
    return this._uppercaseItemName;
  }

  set itemName(value) {
    this._uppercaseItemName = value.toUpperCase();
  }
}

提示

有关使用 getter 和 setter 的另一个示例,请参阅 lwc-recipes 示例存储库中的 和 组件。apiSetterGettertodoList

Boolean 属性

标准 HTML 元素上的布尔属性是通过将属性添加到元素来设置的。如果缺少该属性,则该属性默认为 。因此,属性的默认值始终为 。Lightning Web 组件对布尔属性使用相同的原则。truefalsefalse

静态设置属性

始终将布尔公共属性的默认值设置为 。如果将默认值设置为 instead in ,则无法静态地将值切换为 in markup。falsetruebool.jsfalse

// bool.js
import { LightningElement, api } from "lwc";

export default class Bool extends LightningElement {
  @api show = false;
}

下面是 HTML 文件。

<!-- bool.html -->
<template>
  <p>show value: {show}</p>
</template>

父组件包括 ,它显示默认值 ,因为该属性未添加到 。c-boolfalseshow<c-bool>

重要

在标记中指定的唯一方法是省略 boolean 属性。在标记中设置的计算结果为 。falseshow="false"true

<!-- parent.html -->
<template>
  <c-bool></c-bool>
</template>

若要将属性设置为 ,请将具有空值的属性添加到标记中。此版本的 显示属性的值。showtrueshowc-parenttrueshow

<!-- parent.html -->
<template>
  <c-bool show></c-bool>
</template>

的值是,但属性不会反映在呈现的 HTML 中,除非您通过调用 来显式反映该属性。请参阅将 JavaScript 属性反映为 HTML 属性。showtrueshowsetAttribute

注意

当布尔属性的值为 时,呈现的 HTML 为 。true<my-component my-attribute>

动态设置属性

若要动态切换值,请从父组件传递动态计算值。例如:

<!-- parent.html -->
<template>
  <c-bool show={computedValue}></c-bool>
</template>

使用 JavaScript getter in 返回 的值。parent.js{computedValue}

将 JavaScript 属性反映为 HTML 属性

您可以控制公共 JavaScript 属性是否在 Lightning Web 组件的渲染 HTML 中显示为属性。在创建可访问的组件时,允许属性显示为属性尤为重要,因为屏幕阅读器和其他辅助技术使用 HTML 属性。

默认情况下,所有 HTML 属性都是响应式的。当组件 HTML 中的属性值发生更改时,将重新呈现该组件。

当您通过将某个属性公开为公共属性来控制该属性时,默认情况下,该属性将不再显示在 HTML 输出中。若要将值作为属性传递到呈现的 HTML(以反映属性),请为属性定义 getter 和 setter 并调用该方法。setAttribute()

您还可以在 setter 中执行操作。使用字段保存计算值。

此示例公开为公共属性。它将标题转换为大写,并使用该属性来保存标题的计算值。setter 调用以将属性的值反映到 HTML 属性。title_privateTitlesetAttribute()

// myComponent.js
import { LightningElement, api } from "lwc";

export default class MyComponent extends LightningElement {
  _privateTitle;

  @api
  get title() {
    return this._privateTitle;
  }

  set title(value) {
    this._privateTitle = value.toUpperCase();
    this.setAttribute("title", this._privateTitle);
  }
}
/* parent.html */
<template>
  <c-my-component title="Hover Over the Component to See Me"></c-my-component>
</template>
/* Generated HTML */
<c-my-component title="HOVER OVER THE COMPONENT TO SEE ME">
  <div>Reflecting Attributes Example</div>
</c-my-component>

要确保您了解 JavaScript 属性如何反映到 HTML 属性,请查看不调用 .生成的 HTML 不包含该属性。setAttribute()title

// myComponent.js
import { LightningElement, api } from "lwc";

export default class MyComponent extends LightningElement {
  _privateTitle;

  @api
  get title() {
    return this._privateTitle;
  }

  set title(value) {
    this._privateTitle = value.toUpperCase();
    // this.setAttribute('title', this._privateTitle);
  }
}
/* parent.html */
<template>
  <c-my-component title="Hover Over the Component to See Me"></c-my-component>
</template>
/* Generated HTML */
<c-my-component>
  <div>Reflecting Attributes Example</div>
</c-my-component>

在设置值之前,请检查使用者是否已经设置了该值。

// myComponent.js
import { LightningElement } from "lwc";

export default class MyComponent extends LightningElement {
  connectedCallback() {
    const tabindex = this.getAttribute("tabindex");

    // Set the tabindex to 0 if it hasn’t been set by the consumer.
    if (!tabindex) {
      this.setAttribute("tabindex", "0");
    }
  }
}

在此标记中设置 using 结果。tabindexthis.setAttribute()

<c-my-component tabindex="0"></c-my-component>

要设置这些属性,请使用 .setAttribute()

  • for
  • aria-activedescendant
  • aria-controls
  • aria-describedby
  • aria-details
  • aria-errormessage
  • aria-flowto
  • aria-labelledby
  • aria-owns

要从呈现的 HTML 中隐藏 HTML 属性,请调用 .removeAttribute()

管理 Getter 中的属性依赖关系

HTML 中的属性变成了 JavaScript 中的属性赋值。在这两种情况下,都不能保证分配顺序。要检查是否存在其他属性,请使用 getter。

在模板中使用 getter 引用。不要使用 getter,也不要使用依赖于其他属性中的值的 setter。@api@api@api

假设我们有一个数据表组件,该组件在所选行上显示复选标记。我们有两个独立的属性和 ,它们依赖于另一个属性。rowsselectedRows

<template>
  <c-datatable selected-rows="1,2" rows="1,2,3,4"> </c-datatable>
</template>

由于无法保证接收属性的顺序,因此请使用 getter 来检查依赖关系。

export default class Datatable extends LightningElement {
  @track state = {};

  @api
  get rows() {
    return this.state.rows;
  }

  set rows(value) {
    this.state.rows = value;

    // Check to see if the rows have
    // been marked as selected.
    if (this.state.selectedRows && !this.selectedRowsSet) {
      this.markSelectedRows();
      this.selectedRowsSet = true;
    }
  }

  @api
  set selectedRows(value) {
    this.state.selectedRows = value;

    // If rows haven’t been set,
    // then we can't mark anything
    // as selected.
    if (!this.state.rows) {
      this.selectedRowsSet = false;
      return;
    }

    this.markSelectedRows();
  }

  get selectedRows() {
    return this.state.selectedRows;
  }

  markSelectedRows() {
    // Mark selected rows.
  }
}

使用 getter 和 setter 可确保公共 API 协定易于执行。组件不应更改用 批注的属性的值。@api

如果某些内容在设定的时间依赖于该值,则规范化 setter 中的数据,例如,以编程方式将 CSS 类附加到元素上。返回 getter 中的原始值。规范化也可以在 getter 中完成,这样即使使用者没有设置任何内容,模板也可以访问值。

@track state = {
    selected : false
};

privateSelected = 'false';

@api
get selected() {
    return this.privateSelected;
}
set selected(value) {
    this.privateSelected = value;
    this.state.selected = normalizeBoolean(value)
}