每个组件都必须有一个 JavaScript 文件。Lightning Web 组件中的 JavaScript 文件是 ES6 模块。
共享 JavaScript 代码
若要在组件之间共享代码,请在服务组件中创建一个 ES6 模块,并使用标准 JavaScript 语法导出要共享的变量或函数。
ES6 模块是一个 JavaScript 文件,它显式导出其他模块可以使用的变量或函数。模块使构建代码变得更加容易。
LWC 有两种用于共享代码的模式:
- 创建 JavaScript 文件,这些文件将代码导出到与导入代码的组件相同的文件夹中。使用相对路径导入代码。其他组件无法直接导入此文件。此方法支持在组件中构建代码,而不是与其他组件共享代码。
- 创建一个服务组件(库),该组件是一个组件文件夹,其中包含一个或多个用于导出代码的 JavaScript 文件。为了导入代码,其他组件使用语法。组件只能从主 JavaScript 文件导入代码,该文件与文件夹名称同名。若要共享库中补充 JavaScript 文件中的代码,请从这些文件中导出代码,然后从主 JavaScript 文件中重新导出。请参阅如何在补充 JavaScript 文件中访问导出部分。
c/componentName
此示例使用第一种模式。只能从 和 导入代码。myComponent.js
utils.js
myFunction.js
lwc
└───myComponent
├──myComponent.html
├──myComponent.js
├──myComponent.js-meta.xml
├──myFunction.js
└──utils.js
若要导入代码,请使用相对路径。
// myComponent.js
import { getAmount, calculateCost } from "./utils";
import myFunction from "./myFunction";
提示
JavaScript 文件可以导出命名导出或默认导出。要导入命名导出,代码将使用 中的特定名称。若要导入默认导出,代码可以使用任何名称。在前面的示例中,使用命名导出并使用默认导出。{}
utils.js
myFunction.js
此示例是一个服务组件(库)。文件夹和一个 JavaScript 文件必须具有相同的名称。在此示例中,名称为 。mortgageUtils
lwc
├──mortgageUtils
├──mortgageUtils.js
└──mortgageUtils.js-meta.xml
└───myComponent
├──myComponent.html
├──myComponent.js
└──myComponent.js-meta.xml
若要将代码导入到其他组件中,请使用语法。c/componentName
// myComponent.js
import { getTermOptions, calculateMonthlyPayment } from "c/mortgageUtils";
重要
在语句中,指定要从中导入的文件夹,而不是文件,不要指定文件扩展名。其他组件只能从库的主 JavaScript 文件导入代码,该文件与文件夹同名。组件无法从具有其他名称的补充 JavaScript 文件或嵌套文件夹中的文件导入。若要共享此类文件中的代码,请导出其函数或变量,然后从主 JavaScript 文件中再次导出它们。import
导出默认函数和变量
ES6 模块可以导出单个默认函数或变量。
lwc
└──myComponent
├──myComponent.html
├──myComponent.js
├──myComponent.js-meta.xml
└──myFunction.js
// myFunction.js
export default function () { ··· }
此语法也有效。export
// myFunction.js
export { myFunction as default, … };
若要引用默认导出,导入函数的组件必须在语句中选择一个名称。它不一定是导出的函数或 JavaScript 文件的名称,这只是一个约定。import
// myComponent.js
import myFunction from "./myFunction";
导出命名函数和变量
一个 ES6 模块可以导出多个命名函数或变量。
// mortgage.js
const getTermOptions = () => {
return [
{ label: "20 years", value: 20 },
{ label: "25 years", value: 25 },
];
};
const calculateMonthlyPayment = (principal, years, rate) => {
// Your calculation logic here
};
export { getTermOptions, calculateMonthlyPayment };
导入函数的组件使用导出的名称。
// myComponent.js
import { getTermOptions, calculateMonthlyPayment } from "c/mortgage";
lwc
├──mortgage
├──mortgage.js
└──mortgage.js-meta.xml
└──myComponent
├──myComponent.html
├──myComponent.js
└──myComponent.js-meta.xml
提示
请参阅 lwc-recipes 示例存储库。mortgagemiscSharedJavaScript
LWC 编译器如何解析组件入口点
LWC 模块仅将其一个文件公开为入口点。入口点可以是其 JavaScript 文件或 CSS 文件。LWC 编译器按以下顺序解析模块的入口点。c/moduleNamemoduleName
- 如果存在,则为入口点。
moduleName.js
- 否则,是入口点。
moduleName.css
如果两个文件都未找到,则编译失败。
在此示例中,是模块的入口点:utils.js
c/utils
utils
├──utils.js
└──other.js
将函数从 resolves 导入到 。c/utils
utils.js
// someComponent.js
// This import is valid
import { getSomething } from "c/utils";
所有这些导入都失败:
// someComponent.js
// These imports are invalid
import { getSomething } from "c/utils/utils.js";
import { getSomething } from "c/utils/other.js";
import { getSomething } from "c/utils/utils";
如何访问补充 JavaScript 文件中的导出
补充 JavaScript 文件使用的文件名与组件或模块名称不同。若要从补充 JavaScript 文件访问导出,请使用语法将其导入并在主 JavaScript 文件中重新导出。此语法是 和 的组合。export from
import
export
例如,假设您要从补充文件导出代码。moreUtils.js
lwc
└───utils
├──utils.js
└──moreUtils.js
在 中,导出代码。moreUtils.js
// moreUtils.js
const someFunction = () => {
// logic
};
const someOtherFunction = (arg1 arg2) => {
// logic
};
export{ someFunction, someOtherFunction };
在 中,此语句导出函数并使其可用于其他 Lightning Web 组件。这些函数不可用,除非您还像本页的第一个示例一样导入它们。utils.js
utils.js
// utils.js
export { someFunction, someOtherFunction } from "./moreUtils";
您还可以使用通配符从补充模块导出所有资源。
// utils.js
export * from "./moreUtils";
在组件中,导入函数,就像它们位于 中一样。utils.js
// someComponent.js
import { someFunction, someOtherFunction } from "c/utils";
使用第三方 JavaScript 库
提示
在使用第三方 JavaScript 库之前,我们建议您在 AppExchange 中查找符合您要求的第三方应用程序。或者,检查基本组件是否提供所需的功能。
您可以将第三方 JavaScript 库与 Lightning Web 组件一起使用。例如,使用具有交互式图表和图形的库或降低代码复杂性的库。
- 从第三方库的站点下载库。
- 将库作为静态资源上传到您的 Salesforce 组织,这是 Lightning Web 组件内容安全策略的要求。
- 在扩展的 JavaScript 类中:
LightningElement
- 按库的静态资源名称导入库。
import RESOURCE_NAME from "@salesforce/resourceUrl/RESOURCE_NAME";
例如,如果将静态资源命名为 :myLib
import myLib from "@salesforce/resourceUrl/myLib";
- 从模块导入方法。
platformResourceLoaderimport { loadStyle, loadScript } from "lightning/platformResourceLoader";
请参阅lightning/platformResourceLoader
参考文档。
- 按库的静态资源名称导入库。
- 加载库并在方法中调用其函数。
then()
loadScript(this, myLib + "/myLib.js").then(() => { let result = myLib.myFunction(2, 2); });
重要
如果您的组件在未启用 Lightning Web Security (LWS) 的组织中运行,则您的组件使用的库必须满足 Lightning Locker 要求。请参阅确定 JavaScript 库是否符合 Locker 标准。如果组织使用的是 Lightning Web Security (LWS),则大多数第三方库无需更改即可按预期工作。但是,某些库需要更改才能与 LWS 一起使用。请参阅 LWS 的第三方库注意事项。
使用 JavaScript 操作 DOM
不建议使用 JavaScript 来操作 DOM,因为 Lightning Web 组件引擎可以更高效地执行此操作。但是,有一些第三方 JavaScript 库接管了 DOM。
注意
Salesforce 不提供对第三方 JavaScript 库的支持。演示如何使用第三方 JavaScript 库的文档和示例并不构成对第三方 JavaScript 库的认可。我们建议您查看第三方 JavaScript 库文档,了解使用信息。
如果调用操作 DOM,则样式不会应用于追加的元素。appendChild()
在 Lightning Web 组件中使用这些库时,请添加到要使用 JavaScript 操作的任何 HTML 元素中。当引擎看到该指令时,它会保留封装。lwc:dom="manual"
将指令添加到空的本机 HTML 元素中。组件的所有者调用该元素来手动插入 DOM。lwc:dom="manual"
appendChild()
<template>
<div lwc:dom="manual"></div>
</template>
提示
D3 代码是 lwc-recipes 存储库中的组件。libsD3
示例:D3 JavaScript 库
此组件使用 D3 JavaScript 库创建交互式数据可视化效果。

在 d3js.com 下载 D3。作为静态资源上传到您的 Salesforce 组织。d3.zip
首先,创建一个组件来包含地图。在这里,容器是一个空的。lwc:dom=“manual”
指令告诉 LWC 元素中的 DOM 已手动插入。<svg><svg>
<!-- libsD3.html -->
<template>
<div class="slds-m-around_medium">
<svg class="d3" width={svgWidth} height={svgHeight} lwc:dom="manual"></svg>
</div>
</template>
在组件的 JavaScript 类中,import 和 from .同时导入静态资源。请注意,这是用于加载资源的静态资源引用,并且是上传到 Salesforce 的静态资源的名称。loadStyle
loadScript
lightning/platformResourceLoader
d3
D3
d3
若要创建图形,请在第一次渲染时调用 和 in。使用可确保页面在创建图形之前加载并呈现容器。loadStyle
loadScript
renderedCallback()
renderedCallback()
调用和返回 promise。用于聚合结果,并确保在调用回调之前解析这两个文件。只有在加载完成后,并且只有在没有发生错误时才会调用回调。您可以选择提供回调来处理加载过程中发生的任何潜在错误。loadStyle
loadScript
Promise.all()
then()
catch()
要在 promise 回调中初始化图形,请调用 ,它进入 DOM 并获取对显示图形的容器的引用,这里是一个元素。initializeD3()
<svg>
// libsD3.js
/* global d3 */
import { LightningElement } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { loadScript, loadStyle } from "lightning/platformResourceLoader";
import D3 from "@salesforce/resourceUrl/d3";
import DATA from "./data";
export default class LibsD3 extends LightningElement {
svgWidth = 400;
svgHeight = 400;
d3Initialized = false;
renderedCallback() {
if (this.d3Initialized) {
return;
}
this.d3Initialized = true;
Promise.all([loadScript(this, D3 + "/d3.v5.min.js"), loadStyle(this, D3 + "/style.css")])
.then(() => {
this.initializeD3();
})
.catch((error) => {
this.dispatchEvent(
new ShowToastEvent({
title: "Error loading D3",
message: error.message,
variant: "error",
}),
);
});
}
initializeD3() {
// Example adopted from https://bl.ocks.org/mbostock/2675ff61ea5e063ede2b5d63c08020c7
const svg = d3.select(this.template.querySelector("svg.d3"));
const width = this.svgWidth;
const height = this.svgHeight;
const color = d3.scaleOrdinal(d3.schemeDark2);
const simulation = d3
.forceSimulation()
.force(
"link",
d3.forceLink().id((d) => {
return d.id;
}),
)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
const link = svg
.append("g")
.attr("class", "links")
.selectAll("line")
.data(DATA.links)
.enter()
.append("line")
.attr("stroke-width", (d) => {
return Math.sqrt(d.value);
});
const node = svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(DATA.nodes)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", (d) => {
return color(d.group);
})
.call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
node.append("title").text((d) => {
return d.id;
});
simulation.nodes(DATA.nodes).on("tick", ticked);
simulation.force("link").links(DATA.links);
function ticked() {
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
}
function dragstarted(d) {
if (!d3.event.active) {
simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) {
simulation.alphaTarget(0);
}
d.fx = null;
d.fy = null;
}
}
}
注意
在 Lightning Web 组件中,不能用于查询 DOM 元素。请改用 .例如,此 D3 代码示例使用 .请参阅 DOM 访问遏制。documentthis.templatethis.template.querySelector('svg.d3')
有用的模式
下面是一些用于加载代码的有用模式。此代码加载一个没有 CSS 文件的 JavaScript库。
loadScript(this, RESOURCE_NAME + "/lib.js").then(() => {
/* callback */
});
此代码并行加载多个 JavaScript文件。
Promise.all([
loadScript(this, RESOURCE_NAME + "/lib1.js"),
loadScript(this, RESOURCE_NAME + "/lib2.js"),
loadScript(this, RESOURCE_NAME + "/lib3.js"),
]).then(() => {
/* callback */
});
确定 JavaScript库是否符合保险箱
使用本指南可确定第三方 JavaScript库是否与 Lightning Locker 兼容。
注意
本指南适用于组织使用 Lightning Locker 的情况。如果组织启用了 Lightning Web Security (LWS),则大多数第三方 JavaScript库无需更改即可按预期工作。但是,显式设置的库需要更改才能使用 LWS。请参阅 LWS 的第三方库注意事项。"use strict"
组件使用的任何库都必须满足与组件相同的 Lightning Locker 要求。
- 避免跨命名空间直接操作 DOM。请参阅 DOM 访问遏制。
- 支持 JavaScriptES5 严格模式,如 JavaScript严格模式强制执行中所述。
- 避免使用 Lightning Locker 阻止的 JavaScriptAPI,如 Lightning Locker 工具中所述的 Locker API 查看器所示。
要确定第三方库是否与 Lightning Locker 兼容,我们建议执行以下步骤。
- 创建一个使用该库的小型示例应用。
- 验证库加载时是否正确,以及是否可以调用基本功能。
- 使用启用了 Locker 的 Locker 控制台。
- 检查以下常见违规行为:声明一个变为全局变量的变量严格模式不允许变量成为全局变量。为避免违反此规则,请在库中显式地将库全局附加到 。
windowwindow.myLib = (function () { return { myFunction: function (a, b) { return a * b; }, }; })();
CSP 违规扫描代码以查找 、 或 标记的任何用法。eval()new Function()<script>
DOM 访问冲突如果库尝试对 DOM 进行广泛扫描,而不是仅操作传递给其 API 的元素,则它们将被阻止。如果库操作 DOM,则可以将该指令添加到本机 HTML 元素以允许它,如使用第三方 JavaScript库中所述。lwc:dom="manual"
使用非标准或不受支持的 DOM API请参阅在 Locker API 查看器中标记为“支持”和“不支持”的 API。
如果库不合规,请要求库维护人员更新代码以符合 Lightning Locker,或者,如果项目是开源的,请自己贡献更改。另一种选择是分叉存储库,进行更改,然后构建自己的库版本。
符合 LWS 的 JavaScript库
启用 LWS 后,大多数第三方库都会按预期运行。
但是,某些库需要稍作更改。请参阅 LWS 的第三方库注意事项。
从 JavaScript调用 API
从 Lightning Web 组件调用 Salesforce API 和第三方 API。默认情况下,您无法从 JavaScript代码建立 WebSocket 连接或调用第三方 API。为此,请将远程站点添加为受信任的 URL(以前称为 CSP 受信任的站点)。
Lightning 组件框架使用内容安全策略 (CSP)(一种 W3C 标准)来控制可在页面上加载的内容源。默认 CSP 策略不允许从 JavaScript代码进行 API 调用。通过添加受信任的 URL 来更改策略和 CSP 标头的内容。
Salesforce API 接口
使用 Lightning 数据服务 (LDS) 处理 Salesforce 记录的数据和元数据。Lightning Data Service 建立在公共用户界面 API 之上,但它仅支持 API 的一个子集。该子集涵盖了许多处理数据的典型用例。您无法从 JavaScript代码调用除 LDS 之外的 Salesforce API。
如果 LDS 不支持您要使用的对象,或者如果您想使用其他 Salesforce API,请编写一个 Apex 类。
第三方 API
调用 Salesforce API 或第三方 API 通常需要使用 OAuth 2.0 进行身份验证和授权。出于演示目的,某些 API 还会在未经身份验证和授权的情况下提供其数据,例如使用 Google API 资源管理器。我们建议您查看第三方 API 文档,了解使用详情。
谨慎
如果需要在调用中包含身份验证标头,请使用 Apex HttpRequest
类发送请求。在 JavaScript中提供密钥并不安全。
若要调用第三方 API,必须先将第三方 URL 添加到“设置”中的“受信任的 URL”页。如果要从 获取数据,请将基 URL 添加为受信任的 URL。https://www.example.com/items/v1/brands
https://www.example.com/
使用 Fetch API 进行第三方 API 调用。例如,您可以使用 Fetch API 从 Lightning Web 组件发出 HTTP 请求,然后使用该方法解析 JSON 响应。.json()
async getItems() {
const response = await fetch("http://example.com/items.json");
const items = await response.json();
}
提示
lwc-recipes 存储库中的 miscRestApiCall 组件会向 Google 图书 API 发送请求以执行图书搜索。
Fetch API 返回一个 promise,这在处理异步请求时很有用。使用该方法发出请求并获取资源。仅当请求完全失败时(例如,当用户处于脱机状态或请求超时时)时,才会调用该方法。要处理 4XX 或 5XX 错误,请使用 或在块内检查 response.status
属性。fetch()catch()then()try
async getItems() {
try {
const response = await fetch("http://example.com/items.json");
if (!response.ok) {
throw Error(response);
}
const myItems = await response.json();
} catch (error) {
console.error("There's a problem with your fetch operation:", error);
} finally {
// do something regardless of whether the operation was successful
}
}
有关更多信息,请参阅 MDN Web 文档:使用 Fetch API。或者,您还可以找到符合您要求的第三方 Web 组件 (beta) 或第三方 JavaScript库。
重要
虽然您可以通过受信任的 URL 调用第三方 API,但无法从第三方站点加载 JavaScript资源,即使从受信任的 URL 也是如此。若要使用第三方站点中的 JavaScript库,请将其添加到静态资源,然后将静态资源添加到组件中。从静态资源加载库后,可以正常使用它。
动态实例化组件
动态组件实例化可以帮助您避免加载并不总是需要的大型模块。此外,当基础组件构造函数直到运行时才知道时,可以实例化组件实例。动态导入是一种方便的解决方案,可以使组件更具可定制性。但是,由于它引入了运行时性能开销,它并不总是最佳解决方案,因此不要过度使用它。
如何使用动态 Lightning Web 组件
重要
要动态导入和实例化 Lightning Web 组件,您必须启用 Lightning Web Security。
配置动态组件功能
若要实例化动态组件,组件的配置文件必须包含该功能。例如:lightning__dynamicComponent
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<capabilities>
<capability>lightning__dynamicComponent</capability>
</capabilities>
</LightningComponentBundle>
若要使用此功能,必须将该属性设置为 55.0 或更高版本。apiVersion
有关组件配置文件的详细信息,请参阅组件配置文件。
动态组件语法
若要动态实例化组件,请在组件的 HTML 文件中将托管元素与指令一起使用。<lwc:component>
lwc:is
下面是一个使用 .<lwc:component>
<template>
<div class="container">
<lwc:component lwc:is={componentConstructor}></lwc:component>
</div>
</template>
<lwc:component>
在 DOM 中用作占位符,用于呈现指定的动态组件。您必须与指令一起使用。<lwc:component>
lwc:is
该指令在运行时向托管元素提供导入的构造函数。 接受在运行时解析为构造函数的表达式。lwc:is
<lwc:component>
lwc:is
LightningElement
如果构造函数是伪造的,则不会呈现标记及其所有子级。<lwc:component>
如果定义了表达式值,但未定义构造函数,则会引发错误。LightningElement
在组件的 JavaScript文件中,使用 import()
动态导入语法导入自定义元素。
import { LightningElement } from "lwc";
export default class extends LightningElement {
componentConstructor;
// Use connectedCallback() on the dynamic component
// to signal when it's attached to the DOM
connectedCallback() {
import("c/concreteComponent")
.then(({ default: ctor }) => (this.componentConstructor = ctor))
.catch((err) => console.log("Error importing component"));
}
}
该调用返回一个解析为构造函数的 promise。然后呈现元素而不是占位符。用于动态组件的标记名称是为给定构造函数返回的值。import()
LightningElement
lwc:component
与常规组件类似,动态组件被实例化并附加到 DOM。如果动态组件的构造函数发生更改,则会从 DOM 中删除现有元素。
在此示例中,导入完成后将呈现以下 HTML。
<div class="container">
<c-concrete-component></c-concrete-component>
</div>
除了使用 then()
方法外,还可以使用 and 运算符返回组件构造函数。asyncawait
import { LightningElement } from "lwc";
export default class App extends LightningElement {
componentConstructor;
async connectedCallback() {
const { default: ctor } = await import("c/myComponent");
this.componentConstructor = ctor;
}
}
选择动态组件
必须先将自定义元素附加到 DOM,然后才能选择它。若要选择动态组件,请使用指令或使用分配给该组件的属性,例如类名。lwc:ref
<template>
<lwc:component lwc:is={componentConstructor}
lwc:ref="myCmp">
</lwc:component>
</template>
要确定动态组件是否附加到 DOM,请执行以下操作:
- 在动态组件中用于在附加到 DOM 时发出信号。
connectedCallback
- 在父组件上使用以检测动态组件何时呈现为 DOM。
renderedCallback
import { LightningElement } from "lwc";
export default class extends LightningElement {
componentConstructor;
connectedCallback() {
import("lightning/concreteComponent")
.then(({ default: ctor }) => (this.componentConstructor = ctor))
.catch((err) => console.log("Error importing component"));
}
renderedCallback() {
// this.refs.myCmp will be available on the next rendering cycle after the constructor is set
if (this.refs.myCmp) {
// this.refs.myCmp will contain a reference to the DOM node
console.log(this.refs.myCmp);
}
}
}
分配属性和模板指令
所有可应用于 的受支持的 HTML 属性也可以应用于 。HTMLElement
lwc:component
一些例子包括:
- 标准全局 HTML 属性
- 自定义 HTML 属性,例如
data-*
- 事件侦听器
动态组件的行为类似于标准的 Lightning Web 组件。 支持 HTML 元素的指令,但 .lwc:componentlwc:external
子元素
您可以在动态组件上包含子元素。 首先呈现动态组件,然后呈现其子组件。每次动态组件更改时,都会从 DOM 中删除现有元素及其所有子元素。然后,新的动态组件将与其子组件一起呈现。<lwc:component>
<template>
<lwc:component lwc:is={ctor}>
<span>child</span>
</lwc:component>
</template>
在标记中传递属性
将属性传递给动态组件类似于将属性传递给子组件。在动态组件中,使用属性进行批注并在模板中使用它。@api
// dynamicCmp.js
import { LightningElement, api } from "lwc";
export default class extends LightningElement {
@api text;
}
在占位符组件中,导入自定义元素。
// myApp.js
import { LightningElement } from "lwc";
import DynamicCmp from "c/dynamicCmp";
export default class extends LightningElement {
componentConstructor = DynamicCmp;
}
传入属性的值。text
<!-- myApp.html -->
<template>
<lwc:component lwc:is={componentConstructor} text="I love dynamic components!"></lwc:component>
</template>
在某些情况下,可能无法通过标准标记语法设置动态组件可以接受的所有潜在属性。例如,当要实例化的组件事先不知道,或者要实例化的组件接受一组不同的公共属性时。
在这些特定情况下,[lwc’ 还使元素能够接受在运行时绑定为属性的对象。lwc:spread directive](../create/create-components-spread-properties.md) can be used to dynamically set to dynamic component properties at runtime.
通过使用 注释属性,使属性公开。@api
// dynamicCmp.js
import { LightningElement, api } from "lwc";
export default class extends LightningElement {
@api city;
@api state;
}
使用模板中的属性。
<!-- dynamicCmp.html -->
<template>
<p>{city}, {state}</p>
</template>
像往常一样导入自定义元素,并使用属性名称和值创建对象。childProps
// myApp.js
import { LightningElement } from "lwc";
export default class extends LightningElement {
componentConstructor;
childProps = { city: "San Francisco", state: "CA" };
connectedCallback() {
// import your custom element
}
}
用于传入属性,然后在动态组件上呈现“San Francisco, CA”。lwc:spread
<!-- myApp.html-->
<template>
<lwc:component lwc:is={componentConstructor} lwc:spread={childProps}></lwc:component>
</template>
包中的动态组件
您只能在托管包中使用动态组件。不支持未锁定包中的动态组件。
性能注意事项
由于动态导入本质上是“动态”的,因此框架不会提前预取这些模块,这有时会对用户体验造成不利影响。
使用静态 import 语句时,框架会在运行时在单个 JavaScript文件中提供组件及其所有依赖项。若要获取动态导入的组件,如果内容尚未存储在浏览器 HTTP 缓存中,则框架必须执行网络往返。
在此示例中,组件类与组件类一起作为单个 JavaScript模块提供给浏览器。调用函数时,将在运行时检索组件类。BundleExampleStaticImportDynamicImportloadModule
import { LightningElement } from "lwc";
import StaticImport from "c/static-import";
export default class BundleExample extends LightningElement {
async loadModule() {
const { default: DynamicImport } = await import("c/dynamic-import");
return DynamicImport;
}
}
使用动态组件时,请考虑以下建议。
使动态导入可静态分析
虽然目前尚未实现,但未来的框架优化可以优化可静态分析动态导入的代码。将 JavaScript 字符串文字传递给函数:import()
import("c/analyzable"); // 👍: Statically analyzable
import("c/" + "analyzable"); // 👎: Not statically analyzable
import("c/" + componentName); // 👎: Not statically analyzable
在有些情况下,动态导入总是无法进行静态分析。当通过元数据定义组件名称时,这种情况尤其如此。对于所有其他情况,我们强烈建议将所有动态导入进行静态分析,以便将来进行框架优化。
不要过度使用动态导入
动态导入是一种方便的解决方案,可以使组件更具可定制性。但是,它并不总是最佳解决方案,因为它引入了运行时性能开销。让我们用一些例子来说明潜在的陷阱。
在第一个示例中,我们创建一个图表组件 .它接受图表类型作为公共属性,可以设置为条形图、饼图或折线图。在内部,该组件使用 c/barChart、c/pieChart 或 c/lineChart 组件之一来根据类型呈现图表。c/chart
// 👎 DON'T USE THIS
// Example with non statically analyzable dynamic import:
import { LightningElement, api } from "lwc";
const KNOWN_TYPE = new Set(["bar", "pie", "line"]);
export default class App extends LightningElement {
chartCtor;
_type = "line";
@api
get type() {
return this._type;
}
set type(val) {
if (!KNOWN_TYPE.has(val)) {
console.warn(`Unknown chart type: ${val}`);
}
this._type = val;
const chartComponentName = `c/${val}Chart`;
import(chartComponentName).then(({ default: ChartCtor }) => {
this.chartCtor = ChartCtor;
});
}
}
如果 、 和 的总捆绑包大小较小,则最好将组件更新为使用静态导入,以避免运行时的网络往返。通常,我们建议您从静态导入开始,如果由于导入非严格需要的组件而导致性能成为问题,则使用动态导入。c/barChart
c/pieChart
c/lineChart
c/chart
// 👍 USE THIS
// Example with static imports:
import { LightningElement, api } from "lwc";
import BarChart from "c/barChart";
import PieChart from "c/pieChart";
import LineChart from "c/lineChart";
const KNOWN_TYPE = new Set(["bar", "pie", "line"]);
const CHART_MAPPING = {
bar: BarChart,
pie: PieChart,
line: LineChart,
};
export default class App extends LightningElement {
chartCtor;
_type = "line";
@api
get type() {
return this._type;
}
set type(val) {
if (!KNOWN_TYPE.has(val)) {
console.warn(`Unknown chart type: ${val}`);
}
this._type = val;
this.chartCtor = CHART_MAPPING[val];
}
}
如果静态导入所有三个模块由于包大小增加而对运行时性能产生负面影响,仍然可以更新示例以将非静态可分析的动态导入转换为静态可分析的动态导入。
// 👍 USE THIS
// Example with statically analyzable dynamic import:
import { LightningElement, api } from "lwc";
const KNOWN_TYPE = new Set(["bar", "pie", "line"]);
const CHART_MAPPING = {
bar: () => import("c/barChart"),
pie: () => import("c/pieChart"),
line: () => import("c/lineChart"),
};
export default class App extends LightningElement {
chartCtor;
_type = "line";
@api
get type() {
return this._type;
}
set type(val) {
if (!KNOWN_TYPE.has(val)) {
console.warn(`Unknown chart type: ${val}`);
}
this._type = val;
CHART_MAPPING[val]().then(({ default: ChartCtor }) => {
this.chartCtor = ChartCtor;
});
}
}
这是另一个说明这一原则的例子。在此示例中,我们创建了一个负责呈现实体字段值的组件。由于它是一个泛型组件,因此它接受一个渲染器公共属性,该属性是用于渲染此字段的组件的名称。与前面的示例不同,已知渲染器的列表事先是未知的,因为组件可能接受任何组件名称。c/field
c/field
// 👎 DON'T USE THIS
// Example with component name as prop:
import { LightningElement, api } from "lwc";
export default class Field extends LightningElement {
rendererCtor;
_renderer;
@api
get renderer() {
return this._renderer;
}
set renderer(val) {
this._renderer = val;
import(val).then(({ default: rendererCtor }) => {
this.rendererCtor = rendererCtor;
});
}
}
性能更高的方法是让组件接受渲染器构造函数作为公共属性,而不是渲染器组件名称。仅当组件未暴露给构建器(如 Lightning 应用程序构建器)时,这才有效。c/field
// 👍 USE THIS
// Example with component constructor as prop:
import { LightningElement, api } from "lwc";
export default class Field extends LightningElement {
@api rendererCtor;
}
在这种替代设计中,字段组件将渲染器组件类的加载委托给其父组件。父组件现在可以根据其要求使用静态或动态导入。