使用 Salesforce 数据
Salesforce 提供从简单到定制的多层数据访问方案。始终从最简单的方案开始,按需升级:① 基础组件(lightning-record-*form)→ ② LDS wire adapters(getRecord/getRecords/GraphQL)→ ③ Apex(最大灵活性)。LDS 支持所有自定义对象和 UI API 标准对象(不支持自定义元数据类型),不消耗 API 调用但受记录返回数量等通用限制。
数据指南 —— 选择正确的方案
基础组件(最简单):lightning-record-form——一条语句完成查看/编辑/创建,自动切换模式,使用对象默认布局。指定字段而非布局以获得更好性能。需要自定义布局时用 lightning-record-view-form/lightning-record-edit-form。
LDS Wire Adapters(中等灵活性):@wire 响应式提供数据——数据变更时自动更新组件。GraphQL wire adapter(lightning/graphql)可一次发送多个查询、跨对象查询、父子关系、过滤排序、动态 ID、变更操作,减少请求次数。命令式函数(createRecord/updateRecord/deleteRecord)仅在调用时执行——用于响应用户操作。
Apex(最大灵活性):当 LDS 不支持所需对象或操作时使用——如批量事务、Task/Event 对象。与 LDS 数据不同,Apex 数据不受管理——必须手动刷新(refreshApex() 用于 wire provision 的数据,命令式调用后需 notifyRecordUpdateAvailable() 更新 LDS 缓存)。
重要:Apex 和 LDS 不共享数据缓存——混用可能导致数据不一致。用 SOQL 获取数据时,建议获取和搜索都用 GraphQL wire adapter 以避免 null 值问题。
Lightning Data Service —— 共享缓存与变更检测
LDS 是 Salesforce 数据访问的核心基础设施——加载的记录被缓存并在组件间共享。同一记录无论被多少组件使用,只加载一次——显著提升性能。任何组件修改记录后,所有使用相关 @wire adapter 的组件都收到新值。
变更检测触发条件
- LWC 组件修改了记录
- LDS 缓存条目过期 + 组件的
@wire触发读取(同浏览器、同应用、同用户)
LDS 性能优化
- 渐进加载记录数据
- 客户端缓存结果——从缓存加载比从服务器请求更快
- 当依赖的 Salesforce 数据和元数据变更时失效缓存
- 批量化和去重服务器调用
- 对象和布局元数据有独立超时——存储在持久存储中。布局变更不会立即出现——重新登录或等待超时后刷新
LDS 构建在 User Interface API 之上——响应遵循 CRUD 访问、FLS 设置和共享规则,管理员的布局变更自动反映在响应中。
性能提示:使用返回最少数据的 wire adapter。GraphQL 只返回查询的字段;getRecord 额外返回子关系和布局类型。
比较基础组件 —— 记录表单
| 功能 | record-form | record-view-form | record-edit-form |
|---|---|---|---|
| 创建/编辑/查看记录 | ✅ | 仅查看 | 创建+编辑 |
| 布局类型 | ✅(Full/Compact) | ❌ | ❌ |
| 多列布局 | ✅ | ✅ | ✅ |
| 自定义字段布局 | ❌ | ✅(output-field) | ✅(input-field) |
| 自定义记录数据渲染 | ❌ | ✅(含 getRecord) | ✅ |
| 自动模式切换 | ✅(查看↔编辑) | ❌ | ❌ |
选择指南:大多数场景从 record-form 开始——一条语句覆盖创建/编辑/查看。需要自定义字段布局和渲染时用 view-form/edit-form。需要完全自定义 UI 时用 wire adapters 或 Apex。始终尽量指定字段而非布局——指定布局只有在需要管理员(而非组件)控制字段时才用,且组件必须能处理布局中的每个字段。
加载记录 —— record-form 与 view-form
record-form(最简单——两种模式)
<lightning-record-form record-id={recordId} object-api-name={objectApiName}
fields={fields}></lightning-record-form>
// fields = ["AccountId", "Name", "Title", "Phone", "Email"];
// View 模式(默认——有 recordId):显示 output fields + 编辑图标
// Read-only 模式:仅 output fields,无编辑图标/按钮
record-view-form(自定义布局)
<lightning-record-view-form record-id={recordId} object-api-name="Account">
<div class="slds-grid">
<div class="slds-col slds-size_1-of-2">
<lightning-output-field field-name="Name"></>
<lightning-output-field field-name="Phone"></>
</div>
</div>
</lightning-record-view-form>
getRecord 自定义 UI
@wire(getRecord, { recordId: "$recordId", fields: [NAME_FIELD] }) record;
get nameValue() { return this.record.data ? getFieldValue(this.record.data, NAME_FIELD) : ""; }
<lightning-formatted-text value={nameValue}></lightning-formatted-text>
<!-- 父记录字段:import ... from "@salesforce/schema/Account.Owner.Email" -->
编辑记录 —— record-form 自定义布局与验证
record-form 编辑模式:mode="edit" + layout-type="Compact",或指定具体字段数组。推荐用 @salesforce/schema 导入字段引用(编译时验证)。
record-edit-form 自定义布局:lightning-input-field 自动根据字段类型渲染输入控件。包含 lightning-messages 自动显示错误。四个自定义事件:error(服务器端错误)、load(数据加载)、submit(表单提交)、success(保存成功)。
重置表单:this.template.querySelectorAll("lightning-input-field").forEach(f => f.reset())
自定义验证:lightning-input-field 不支持客户端自定义验证——需要时用 lightning-input + setCustomValidity() + reportValidity()。在 onsubmit 中阻止默认行为→验证→通过则调用 this.template.querySelector("lightning-record-edit-form").submit(fields)。
创建记录 —— 基础组件与预填充
record-form 创建模式:不传 record-id——组件自动切换到创建模式。
<lightning-record-form object-api-name={accountObject} fields={myFields}
onsuccess={handleAccountCreated}></lightning-record-form>
// myFields = [NAME_FIELD, WEBSITE_FIELD]; // @salesforce/schema 导入
record-edit-form 预填充:用 value 属性预设字段值——静态值直接在标记中,动态值通过 JS 属性绑定(value={myValue})。
构建自定义 UI —— 命令式 createRecord
当 lightning-record*form 不够灵活时,使用 lightning/uiRecordApi 命令式函数:
import { createRecord } from "lightning/uiRecordApi";
import ACCOUNT_OBJECT from "@salesforce/schema/Account";
import NAME_FIELD from "@salesforce/schema/Account.Name";
async createAccount() {
const fields = {};
fields[NAME_FIELD.fieldApiName] = this.name;
const recordInput = { apiName: ACCOUNT_OBJECT.objectApiName, fields };
try {
const account = await createRecord(recordInput);
this.accountId = account.id;
this.dispatchEvent(new ShowToastEvent({ title: 'Success', message: 'Account created', variant: 'success' }));
} catch (error) {
this.dispatchEvent(new ShowToastEvent({ title: 'Error', message: reduceErrors(error).join(', '), variant: 'error' }));
}
}
关键要点:① createRecord(recordInput) 返回 Promise——创建成功时解析为包含 account.id 的记录对象。② 使用 reduceErrors 工具函数从错误对象中提取可读错误消息(参考 lwc-recipes ldsUtils)。③ 同理 updateRecord(recordInput) 和 deleteRecord(recordId) 用于更新和删除。④ 命令式调用是显式操作——不自动提供数据响应式更新。
感谢阅读本指南。如需继续学习,请参阅下一章:在表格中显示记录数据。