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, base record form components, or Apex.deleteRecord - Fixing stale-data bugs after record mutation.
- Adding schema imports () to replace hard-coded field/object names.
@salesforce/schema/...
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、基础记录表单组件或Apex之间做选择。deleteRecord - 修复记录变更后的过期数据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 imports (Setup → Object Manager →
@salesforce/schema→ Details → API Name; or, when GraphQL serves the read, an SDL pulled from the target org).<Object>
- 组件路径。
- 了解组件的数据操作(读取/写入/两者皆有)以及是否已使用Apex。
- 能够访问组织的schema以进行导入(设置→对象管理器→
@salesforce/schema→详情→API名称;或当GraphQL用于读取时,从目标组织获取SDL)。<Object>
Knowledge Bases
知识库
- references/lds-expert.md — authoritative LDS knowledge (patterns, adapters, caching, mutation flows).
- references/lds-data-consistency.md — cache invalidation, ,
refreshApex, wire result propagation.notifyRecordUpdateAvailable - references/lds-referential-integrity.md — imports, field constants, object-name resolution, and their propagation through refactors.
@salesforce/schema
Per-adapter API reference — references/adapter-apis.md holds Syntax / Parameters / Returns / Usage for every UI API adapter, grouped by family (, , , ). Each adapter is a block; grep for the backticked name (e.g. ) to jump to its entry. Read this before wiring an adapter; do not paraphrase from memory.
uiRecordApisuiListsApisuiRelatedListApisuiObjectInfoApis# `<name>`# `getRecord`Type catalog — references/wire-adapter-types.md holds every type the adapters return (, , , etc.), grouped by category and rendered with the same formatter the legacy MCP tool used. Grep for to jump to a specific entry.
RecordObjectInfoFieldValue## <TypeName>Read the applicable reference before editing code.
- references/lds-expert.md——权威LDS知识(模式、适配器、缓存、变更流程)。
- references/lds-data-consistency.md——缓存失效、、
refreshApex、wire结果传播。notifyRecordUpdateAvailable - references/lds-referential-integrity.md——导入、字段常量、对象名称解析及其在重构中的传播。
@salesforce/schema
各适配器API参考——references/adapter-apis.md包含每个UI API适配器的语法/参数/返回值/用法,按家族分组(、、、)。每个适配器以块呈现;搜索带反引号的名称(如)可快速定位其条目。在配置适配器前请阅读此文档,请勿凭记忆改写。
uiRecordApisuiListsApisuiRelatedListApisuiObjectInfoApis# `<name>`# `getRecord`类型目录——references/wire-adapter-types.md包含适配器返回的所有类型(、、等),按类别分组并使用与旧版MCP工具相同的格式化方式呈现。搜索可快速定位特定类型条目。
RecordObjectInfoFieldValue## <TypeName>编辑代码前请阅读对应的参考文档。
Core Principles
核心原则
- Prefer LDS/UIAPI for CRUD on standard and custom objects. Use Apex only when business logic or bulk operations exceed LDS capabilities.
- Always keep rendered data fresh with or
refreshApex(wiredResult)after any mutation.notifyRecordUpdateAvailable([{ recordId }]) - Import object and field references from — not string literals. This protects the component against metadata renames.
@salesforce/schema - Favor base record form components (,
lightning-record-form,lightning-record-edit-form) for single-record UIs. They ship with validation, SLDS styling, accessibility, and field-level security.lightning-record-view-form
- 优先使用LDS/UIAPI处理标准和自定义对象的CRUD操作。仅当业务逻辑或批量操作超出LDS能力时,才使用Apex。
- 在任何变更操作后,务必通过或
refreshApex(wiredResult)确保渲染数据的新鲜度。notifyRecordUpdateAvailable([{ recordId }]) - 从导入对象和字段引用——而非使用字符串字面量。这可防止组件因元数据重命名而失效。
@salesforce/schema - 对于单记录UI,优先使用基础记录表单组件(、
lightning-record-form、lightning-record-edit-form)。这些组件内置验证、SLDS样式、可访问性和字段级安全功能。lightning-record-view-form
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, orlightning-record-edit-formwould suffice?lightning-record-view-form - 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 without schema imports?
record.data.fields.Name.value - Does the JS file lack any import even though it interacts with Salesforce fields?
@salesforce/schema
- 对象或字段API名称是否以硬编码字符串形式引用?
- 在模板中,是否通过这样的表达式直接访问字段值,而未使用schema导入?
record.data.fields.Name.value - JS文件是否未包含任何导入,即使它与Salesforce字段交互?
@salesforce/schema
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, or a base record form could handle?updateRecord - 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, Apex imperative).deleteRecord - 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、Apex命令式调用)。deleteRecord - 读取/写入操作、目标对象、字段,以及写入后的刷新路径是否已配置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 , or as a compliant entry under . Finish with a line listing counts and a one-paragraph narrative.
## LDS Best Practices## Sections checked (no issue)## SummaryReport shape:
markdown
undefined依次检查第1至第4部分。对于每个部分,判断是否适用于当前审查的组件,并将结果记录在报告中。使用以下格式——每个部分必须恰好出现在一个区块中:要么作为下的问题(违规),要么作为下的合规条目。最后添加部分,列出数量统计和一段说明性文字。
## LDS最佳实践## 已检查部分(无问题)## 摘要报告格式:
markdown
undefinedLDS 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, field arrays, calls, and base-component / attributes.
@wiregetFieldValue(record, NAME_FIELD)object-api-namefieldsFull 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配置、字段数组、调用、基础组件的/属性。
@wiregetFieldValue(record, NAME_FIELD)object-api-namefields完整规则:references/lds-referential-integrity.md。
Step 4 — Apply data-consistency fixes
步骤4 — 应用数据一致性修复
- After imperative LDS mutation (,
updateRecord,createRecord), dispatch a refresh:deleteRecordjavascriptimport { 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 () so
this.wiredRecord = result; return result.data;can target them.refreshApex - Base form components refresh themselves; no manual refresh needed.
Full rules: references/lds-data-consistency.md.
- 在命令式LDS变更(、
updateRecord、createRecord)后,触发刷新:deleteRecordjavascriptimport { 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 → (with
getRecord+ schema imports).fields - Single-record update → or
updateRecord.lightning-record-edit-form - Single-record create → or
createRecordwithlightning-record-form.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. ) — it has the authoritative parameters, returns, and usage. For paths, confirm the exact API name in Setup → Object Manager → → Details → API Name. For unfamiliar return types, grep references/wire-adapter-types.md for the type name.
# `getRecord`@salesforce/schema/<Object>.<Field><Object>- 单记录读取 → (配合
getRecord+schema导入)。fields - 单记录更新 → 或
updateRecord。lightning-record-edit-form - 单记录创建 → 或
createRecord的mode="edit"。lightning-record-form - 关联记录读取 → 。
getRelatedListRecords - 选择列表值 → 。
getPicklistValues - 对象元数据 → /
getObjectInfo。getObjectInfos
若不确定适配器格式,在references/adapter-apis.md中搜索带反引号的适配器名称(如)——该文档包含权威的参数、返回值和用法说明。对于路径,在设置→对象管理器→→详情→API名称中确认准确的API名称。对于不熟悉的返回类型,在references/wire-adapter-types.md中搜索类型名称。
# `getRecord`@salesforce/schema/<Object>.<Field><Object>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 (called exactly once per mutation).
refreshApex
- 组件文件中无硬编码API名称残留。
- 每个写入路径都有对应的刷新路径(或使用了基础表单组件)。
- 无同一记录同时通过Apex和UIAPI重复读取的情况。
- 现有Jest测试通过;添加刷新流程的测试覆盖(每次变更调用恰好一次)。
refreshApex
Cross-References
交叉引用
- Related skills:
- — when the review surfaces the need to regenerate rather than patch the component.
experience-lwc-generate - — for SLDS class / design-token cleanup surfaced by the LDS review.
design-systems-slds-apply
- 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 - ——用于LDS审查发现的SLDS类/设计令牌清理工作。
design-systems-slds-apply
- 相关独立工作流(目前需单独处理):
- 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 imports — they should cover every field/object the component references.
@salesforce/schema/ - Grep for string literals that look like API names (,
'Account') — none should appear in wire configs or field arrays.'Name' - Trace every mutation call to a refresh call (either ,
refreshApex, or a base form handling it internally).notifyRecordUpdateAvailable - Confirm Apex is only used where UIAPI can't satisfy the requirement (bulk, complex joins, custom logic).
- 搜索导入项——它们应覆盖组件引用的所有字段/对象。
@salesforce/schema/ - 搜索类似API名称的字符串字面量(、
'Account')——wire配置或字段数组中不应存在此类内容。'Name' - 跟踪每个变更调用对应的刷新调用(、
refreshApex或基础表单内部处理)。notifyRecordUpdateAvailable - 确认仅在UIAPI无法满足需求时(批量操作、复杂关联、自定义逻辑)才使用Apex。