experience-lds-best-practices-apply

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- adk-managed-skill -->
<!-- adk-managed-skill -->

Applying LDS Best Practices

应用LDS最佳实践

Apply the Lightning Data Service guidelines to a Lightning Web Component. Three pillars: data consistency, referential integrity, and UIAPI vs Apex. Focused on the UI API path — GraphQL and upstream data-requirements analysis are handled out-of-band.
将Lightning Data Service指南应用于Lightning Web Component。三大核心:数据一致性引用完整性UIAPI与Apex的选择。重点关注UI API路径——GraphQL与上游数据需求分析需单独处理。

When to Use

适用场景

  • Reviewing a component's data layer for LDS compliance (hand-rolled forms, stringly-typed field names, un-synchronized Apex + LDS, Apex overuse).
  • Implementing CRUD on standard or custom objects.
  • Deciding between
    getRecord
    ,
    getRecords
    ,
    createRecord
    ,
    updateRecord
    ,
    deleteRecord
    , base record form components, or Apex.
  • Fixing stale-data bugs after record mutation.
  • Adding schema imports (
    @salesforce/schema/...
    ) to replace hard-coded field/object names.
Do NOT use this skill for:
  • GraphQL query/mutation generation — handled out-of-band today.
  • Upstream data-requirements discovery — handled out-of-band today.
  • SLDS class / design-token work (use
    design-systems-slds-apply
    ).
  • Accessibility, security, or RTL review — those are separate passes run with their own tooling.
  • 审查组件的数据层是否符合LDS规范(自定义表单、字符串类型字段名、未同步的Apex+LDS、过度使用Apex)。
  • 对标准或自定义对象实施CRUD操作。
  • getRecord
    getRecords
    createRecord
    updateRecord
    deleteRecord
    、基础记录表单组件或Apex之间做选择。
  • 修复记录变更后的过期数据bug。
  • 添加
    @salesforce/schema/...
    导入以替换硬编码的字段/对象名称。
请勿在以下场景使用本技能:
  • GraphQL查询/变更生成——目前需单独处理。
  • 上游数据需求调研——目前需单独处理。
  • SLDS类/设计令牌相关工作(使用
    design-systems-slds-apply
    )。
  • 可访问性、安全或RTL审查——这些需通过各自工具单独进行。

Prerequisites

前置条件

  • Component path.
  • Understanding of the component's data operations (read / write / both) and whether Apex is already involved.
  • Access to the org's schema for
    @salesforce/schema
    imports (Setup → Object Manager →
    <Object>
    → Details → API Name; or, when GraphQL serves the read, an SDL pulled from the target org).
  • 组件路径。
  • 了解组件的数据操作(读取/写入/两者皆有)以及是否已使用Apex。
  • 能够访问组织的schema以进行
    @salesforce/schema
    导入(设置→对象管理器→
    <Object>
    →详情→API名称;或当GraphQL用于读取时,从目标组织获取SDL)。

Knowledge Bases

知识库

  • references/lds-expert.md — authoritative LDS knowledge (patterns, adapters, caching, mutation flows).
  • references/lds-data-consistency.md — cache invalidation,
    refreshApex
    ,
    notifyRecordUpdateAvailable
    , wire result propagation.
  • references/lds-referential-integrity.md
    @salesforce/schema
    imports, field constants, object-name resolution, and their propagation through refactors.
Per-adapter API referencereferences/adapter-apis.md holds Syntax / Parameters / Returns / Usage for every UI API adapter, grouped by family (
uiRecordApis
,
uiListsApis
,
uiRelatedListApis
,
uiObjectInfoApis
). Each adapter is a
# `<name>`
block; grep for the backticked name (e.g.
# `getRecord`
) to jump to its entry. Read this before wiring an adapter; do not paraphrase from memory.
Type catalogreferences/wire-adapter-types.md holds every type the adapters return (
Record
,
ObjectInfo
,
FieldValue
, etc.), grouped by category and rendered with the same formatter the legacy MCP tool used. Grep for
## <TypeName>
to jump to a specific entry.
Read the applicable reference before editing code.
  • references/lds-expert.md——权威LDS知识(模式、适配器、缓存、变更流程)。
  • references/lds-data-consistency.md——缓存失效、
    refreshApex
    notifyRecordUpdateAvailable
    、wire结果传播。
  • references/lds-referential-integrity.md——
    @salesforce/schema
    导入、字段常量、对象名称解析及其在重构中的传播。
各适配器API参考——references/adapter-apis.md包含每个UI API适配器的语法/参数/返回值/用法,按家族分组(
uiRecordApis
uiListsApis
uiRelatedListApis
uiObjectInfoApis
)。每个适配器以
# `<name>`
块呈现;搜索带反引号的名称(如
# `getRecord`
)可快速定位其条目。在配置适配器前请阅读此文档,请勿凭记忆改写。
类型目录——references/wire-adapter-types.md包含适配器返回的所有类型(
Record
ObjectInfo
FieldValue
等),按类别分组并使用与旧版MCP工具相同的格式化方式呈现。搜索
## <TypeName>
可快速定位特定类型条目。
编辑代码前请阅读对应的参考文档。

Core Principles

核心原则

  1. Prefer LDS/UIAPI for CRUD on standard and custom objects. Use Apex only when business logic or bulk operations exceed LDS capabilities.
  2. Always keep rendered data fresh with
    refreshApex(wiredResult)
    or
    notifyRecordUpdateAvailable([{ recordId }])
    after any mutation.
  3. Import object and field references from
    @salesforce/schema
    — not string literals. This protects the component against metadata renames.
  4. Favor base record form components (
    lightning-record-form
    ,
    lightning-record-edit-form
    ,
    lightning-record-view-form
    ) for single-record UIs. They ship with validation, SLDS styling, accessibility, and field-level security.
  1. 优先使用LDS/UIAPI处理标准和自定义对象的CRUD操作。仅当业务逻辑或批量操作超出LDS能力时,才使用Apex。
  2. 在任何变更操作后,务必通过
    refreshApex(wiredResult)
    notifyRecordUpdateAvailable([{ recordId }])
    确保渲染数据的新鲜度。
  3. @salesforce/schema
    导入对象和字段引用——而非使用字符串字面量。这可防止组件因元数据重命名而失效。
  4. 对于单记录UI,优先使用基础记录表单组件(
    lightning-record-form
    lightning-record-edit-form
    lightning-record-view-form
    )。这些组件内置验证、SLDS样式、可访问性和字段级安全功能。

Review Checklist

审查清单

Answer Yes / No to each. Any Yes triggers a refactor.
对每个问题回答是/否。任何都需要进行重构。

1. Hand-rolling forms instead of base components

1. 手动构建表单而非使用基础组件

  • Does the component implement a custom form for single-record CRUD where
    lightning-record-form
    ,
    lightning-record-edit-form
    , or
    lightning-record-view-form
    would suffice?
  • Does validation logic duplicate what base record form components provide natively?
  • Are standard SLDS styles recreated manually instead of leveraging the styling baked into base components?
  • 组件是否为单记录CRUD实现了自定义表单,而
    lightning-record-form
    lightning-record-edit-form
    lightning-record-view-form
    即可满足需求?
  • 验证逻辑是否重复了基础记录表单组件原生提供的功能?
  • 是否手动重新创建了标准SLDS样式,而非利用基础组件内置的样式?

2. Not importing references

2. 未导入引用

  • Are object or field API names referenced as hard-coded strings?
  • In templates, are field values accessed directly via expressions like
    record.data.fields.Name.value
    without schema imports?
  • Does the JS file lack any
    @salesforce/schema
    import even though it interacts with Salesforce fields?
  • 对象或字段API名称是否以硬编码字符串形式引用?
  • 在模板中,是否通过
    record.data.fields.Name.value
    这样的表达式直接访问字段值,而未使用schema导入?
  • JS文件是否未包含任何
    @salesforce/schema
    导入,即使它与Salesforce字段交互?

3. Mixing Apex and LDS without synchronization

3. 混合使用Apex与LDS但未同步

  • Does the component read via LDS and mutate the same record through Apex without a subsequent cache refresh?
  • Does it fetch through Apex yet rely on the LDS cache for display without synchronizing after updates?
  • Do multiple data sources touch the same object without an explicit refresh strategy?
  • 组件是否通过LDS读取数据,又通过Apex变更同一记录但未刷新缓存?
  • 是否通过Apex获取数据,却依赖LDS缓存进行显示,且在更新后未同步?
  • 多个数据源是否操作同一对象但未设置明确的刷新策略?

4. Overusing Apex

4. 过度使用Apex

  • Does the component call Apex solely to retrieve or update a single record that
    getRecord
    ,
    updateRecord
    , or a base record form could handle?
  • Is Apex used to run a simple SOQL query whose fields are available through standard LDS wire adaptors?
  • Are custom Apex methods present for basic CRUD while no LDS/UIAPI calls appear in the code?
  • 组件是否仅为检索或更新单条记录而调用Apex,而
    getRecord
    updateRecord
    或基础记录表单即可处理?
  • 是否使用Apex执行简单SOQL查询,而该查询的字段可通过标准LDS wire适配器获取?
  • 是否存在用于基础CRUD的自定义Apex方法,而代码中未出现任何LDS/UIAPI调用?

Workflow

工作流程

Step 1 — Inventory the data layer

步骤1 — 梳理数据层

List every data operation in the component:
  • Wire adapters (
    @wire(getRecord, …)
    ,
    @wire(getRecords, …)
    ,
    @wire(someApexMethod, …)
    ).
  • Imperative calls (
    updateRecord
    ,
    createRecord
    ,
    deleteRecord
    , Apex imperative).
  • Reads vs writes, target object(s), fields, and whether the refresh path after writes is wired.
列出组件中的所有数据操作:
  • Wire适配器(
    @wire(getRecord, …)
    @wire(getRecords, …)
    @wire(someApexMethod, …)
    )。
  • 命令式调用(
    updateRecord
    createRecord
    deleteRecord
    、Apex命令式调用)。
  • 读取/写入操作、目标对象、字段,以及写入后的刷新路径是否已配置wire。

Step 2 — Run the four-section checklist

步骤2 — 执行四部分清单检查

Walk sections §1–§4 in order. For each section, decide whether it applies to the component under review and record the result in the report. Use the shape below — every section must appear exactly once, either as an issue (violation) under
## LDS Best Practices
, or as a compliant entry under
## Sections checked (no issue)
. Finish with a
## Summary
line listing counts and a one-paragraph narrative.
Report shape:
markdown
undefined
依次检查第1至第4部分。对于每个部分,判断是否适用于当前审查的组件,并将结果记录在报告中。使用以下格式——每个部分必须恰好出现在一个区块中:要么作为
## LDS最佳实践
下的问题(违规),要么作为
## 已检查部分(无问题)
下的合规条目。最后添加
## 摘要
部分,列出数量统计和一段说明性文字。
报告格式:
markdown
undefined

LDS Best Practices

LDS最佳实践

  • §<N> <section title><file>:<lines> Issue: <what is wrong, specifically citing the pattern in the code> Fix: <the corrective change, naming the exact import / API to use> Applied: <yes | no>
  • §<N> <章节标题> — <文件>:<行号> 问题:<具体问题,需引用代码中的模式> 修复方案:<具体修正措施,指明要使用的导入/API> 是否已应用:<是 | 否>

Sections checked (no issue)

已检查部分(无问题)

  • §<N> <section title><file>:<lines> Status: Compliant (no action). Evidence: <what in the code makes this section compliant — cite lines, imports, and the base component or schema token being used>
  • §<N> <章节标题> — <文件>:<行号> 状态:合规(无需操作)。 依据:<代码中哪些内容证明本部分合规——引用行号、导入项、使用的基础组件或schema令牌>

Summary

摘要

  • <X> issue(s) found; <Y> fixed; <Z> deferred.
  • <one-paragraph narrative of the review — what the component does, why the flagged issues matter, and why the compliant sections are compliant.>

Rules for producing this report:

- Every one of §1–§4 must appear in exactly one of the two blocks. Do not omit a section because it is compliant; record it with evidence under `## Sections checked (no issue)`.
- Under `## LDS Best Practices`, only list actual violations. If there are no violations, write "No best-practice issues found." as the first line, then move every section to the compliant block.
- Cite specific file paths and line ranges from the component under review — never generic references.
- Do not invent additional sections beyond §1–§4; downstream a11y / RTL / security reviews run as separate workflows and have their own reports.
  • 发现<X>个问题;已修复<Y>个;延迟处理<Z>个。
  • <一段关于审查的说明文字——组件功能、标记问题的重要性、合规部分为何合规。>

生成报告的规则:

- 第1至第4部分必须恰好出现在两个区块中的一个。请勿因某部分合规而省略,需将其记录在`## 已检查部分(无问题)`下并提供依据。
- 在`## LDS最佳实践`下,仅列出实际违规项。若无违规,第一行写“未发现最佳实践问题。”,然后将所有部分移至合规区块。
- 引用审查组件的具体文件路径和行号范围——请勿使用通用引用。
- 请勿添加第1至第4部分之外的章节;后续的可访问性/RTL/安全审查为独立工作流,有各自的报告。

Step 3 — Apply referential-integrity fixes

步骤3 — 应用引用完整性修复

For every hard-coded API name:
javascript
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
Use these constants everywhere the object or field is referenced — wire configs,
@wire
field arrays,
getFieldValue(record, NAME_FIELD)
calls, and base-component
object-api-name
/
fields
attributes.
Full rules: references/lds-referential-integrity.md.
对于每个硬编码的API名称:
javascript
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
在所有引用对象或字段的地方使用这些常量——wire配置、
@wire
字段数组、
getFieldValue(record, NAME_FIELD)
调用、基础组件的
object-api-name
/
fields
属性。
完整规则:references/lds-referential-integrity.md

Step 4 — Apply data-consistency fixes

步骤4 — 应用数据一致性修复

  • After imperative LDS mutation (
    updateRecord
    ,
    createRecord
    ,
    deleteRecord
    ), dispatch a refresh:
    javascript
    import { updateRecord, getRecord } from 'lightning/uiRecordApi';
    import { refreshApex } from '@salesforce/apex';
    
    async handleSave() {
        await updateRecord({ fields: { Id: this.recordId, Name: this.name } });
        await refreshApex(this.wiredRecord);
    }
  • After Apex mutation of a record the cache holds, prefer:
    javascript
    import { notifyRecordUpdateAvailable } from 'lightning/uiRecordApi';
    await notifyRecordUpdateAvailable([{ recordId: this.recordId }]);
  • Keep a reference to wire results (
    this.wiredRecord = result; return result.data;
    ) so
    refreshApex
    can target them.
  • Base form components refresh themselves; no manual refresh needed.
Full rules: references/lds-data-consistency.md.
  • 命令式LDS变更(
    updateRecord
    createRecord
    deleteRecord
    )后,触发刷新:
    javascript
    import { updateRecord, getRecord } from 'lightning/uiRecordApi';
    import { refreshApex } from '@salesforce/apex';
    
    async handleSave() {
        await updateRecord({ fields: { Id: this.recordId, Name: this.name } });
        await refreshApex(this.wiredRecord);
    }
  • 在缓存中记录的记录被Apex变更后,优先使用:
    javascript
    import { notifyRecordUpdateAvailable } from 'lightning/uiRecordApi';
    await notifyRecordUpdateAvailable([{ recordId: this.recordId }]);
  • 保留wire结果的引用(
    this.wiredRecord = result; return result.data;
    ),以便
    refreshApex
    可以定位它们。
  • 基础表单组件会自动刷新;无需手动刷新。
完整规则:references/lds-data-consistency.md

Step 5 — Replace Apex with UIAPI where applicable

步骤5 — 在适用场景下用UIAPI替换Apex

  • Single-record read →
    getRecord
    (with
    fields
    + schema imports).
  • Single-record update →
    updateRecord
    or
    lightning-record-edit-form
    .
  • Single-record create →
    createRecord
    or
    lightning-record-form
    with
    mode="edit"
    .
  • Related-record read →
    getRelatedListRecords
    .
  • Picklist values →
    getPicklistValues
    .
  • Object metadata →
    getObjectInfo
    /
    getObjectInfos
    .
When in doubt about adapter shape, grep references/adapter-apis.md for the backticked adapter name (e.g.
# `getRecord`
) — it has the authoritative parameters, returns, and usage. For
@salesforce/schema/<Object>.<Field>
paths, confirm the exact API name in Setup → Object Manager →
<Object>
→ Details → API Name. For unfamiliar return types, grep references/wire-adapter-types.md for the type name.
  • 单记录读取 →
    getRecord
    (配合
    fields
    +schema导入)。
  • 单记录更新 →
    updateRecord
    lightning-record-edit-form
  • 单记录创建 →
    createRecord
    mode="edit"
    lightning-record-form
  • 关联记录读取 →
    getRelatedListRecords
  • 选择列表值 →
    getPicklistValues
  • 对象元数据 →
    getObjectInfo
    /
    getObjectInfos
若不确定适配器格式,在references/adapter-apis.md中搜索带反引号的适配器名称(如
# `getRecord`
)——该文档包含权威的参数、返回值和用法说明。对于
@salesforce/schema/<Object>.<Field>
路径,在设置→对象管理器→
<Object>
→详情→API名称中确认准确的API名称。对于不熟悉的返回类型,在references/wire-adapter-types.md中搜索类型名称。

Step 6 — Verify

步骤6 — 验证

  • No hardcoded API names remain in the component files.
  • Every write path has a matching refresh path (or uses a base form component).
  • No duplicate reads of the same record via both Apex and UIAPI.
  • Existing Jest tests pass; add coverage for the refresh flow (
    refreshApex
    called exactly once per mutation).
  • 组件文件中无硬编码API名称残留。
  • 每个写入路径都有对应的刷新路径(或使用了基础表单组件)。
  • 无同一记录同时通过Apex和UIAPI重复读取的情况。
  • 现有Jest测试通过;添加刷新流程的测试覆盖(每次变更调用
    refreshApex
    恰好一次)。

Cross-References

交叉引用

  • Related skills:
    • experience-lwc-generate
      — when the review surfaces the need to regenerate rather than patch the component.
    • design-systems-slds-apply
      — for SLDS class / design-token cleanup surfaced by the LDS review.
  • Adjacent (out-of-band today):
    • GraphQL query/mutation authoring, upstream data-requirements analysis, and the security / RTL / a11y review passes run as separate workflows with their own tooling.
  • 相关技能:
    • experience-lwc-generate
      ——当审查表明需要重新生成而非修补组件时使用。
    • design-systems-slds-apply
      ——用于LDS审查发现的SLDS类/设计令牌清理工作。
  • 相关独立工作流(目前需单独处理):
    • GraphQL查询/变更编写、上游数据需求分析、安全/RTL/可访问性审查为独立工作流,有各自的工具。

Examples

示例

Base-component first (preferred)
html
<template>
    <lightning-record-form
        record-id={recordId}
        object-api-name="Account"
        fields={fields}
        mode="edit"
        onsuccess={handleSuccess}>
    </lightning-record-form>
</template>
javascript
import { LightningElement, api } from 'lwc';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class AccountEditor extends LightningElement {
    @api recordId;
    fields = [NAME_FIELD, INDUSTRY_FIELD];

    handleSuccess() {
        this.dispatchEvent(new CustomEvent('saved'));
    }
}
Imperative update with refresh
javascript
import { LightningElement, api, wire } from 'lwc';
import { getRecord, updateRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';

export default class RenameAccount extends LightningElement {
    @api recordId;
    wiredRecord;

    @wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME] })
    wired(result) {
        this.wiredRecord = result;
    }

    async handleRename(event) {
        await updateRecord({ fields: { Id: this.recordId, Name: event.detail } });
        await refreshApex(this.wiredRecord);
    }
}
优先使用基础组件
html
<template>
    <lightning-record-form
        record-id={recordId}
        object-api-name="Account"
        fields={fields}
        mode="edit"
        onsuccess={handleSuccess}>
    </lightning-record-form>
</template>
javascript
import { LightningElement, api } from 'lwc';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class AccountEditor extends LightningElement {
    @api recordId;
    fields = [NAME_FIELD, INDUSTRY_FIELD];

    handleSuccess() {
        this.dispatchEvent(new CustomEvent('saved'));
    }
}
带刷新的命令式更新
javascript
import { LightningElement, api, wire } from 'lwc';
import { getRecord, updateRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';

export default class RenameAccount extends LightningElement {
    @api recordId;
    wiredRecord;

    @wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME] })
    wired(result) {
        this.wiredRecord = result;
    }

    async handleRename(event) {
        await updateRecord({ fields: { Id: this.recordId, Name: event.detail } });
        await refreshApex(this.wiredRecord);
    }
}

Verification

验证

  • Grep for
    @salesforce/schema/
    imports — they should cover every field/object the component references.
  • Grep for string literals that look like API names (
    'Account'
    ,
    'Name'
    ) — none should appear in wire configs or field arrays.
  • Trace every mutation call to a refresh call (either
    refreshApex
    ,
    notifyRecordUpdateAvailable
    , or a base form handling it internally).
  • Confirm Apex is only used where UIAPI can't satisfy the requirement (bulk, complex joins, custom logic).
  • 搜索
    @salesforce/schema/
    导入项——它们应覆盖组件引用的所有字段/对象。
  • 搜索类似API名称的字符串字面量(
    'Account'
    'Name'
    )——wire配置或字段数组中不应存在此类内容。
  • 跟踪每个变更调用对应的刷新调用(
    refreshApex
    notifyRecordUpdateAvailable
    或基础表单内部处理)。
  • 确认仅在UIAPI无法满足需求时(批量操作、复杂关联、自定义逻辑)才使用Apex。