salesforce-component-standards

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Salesforce Component Quality Standards

Salesforce组件质量标准

Apply these checks to every LWC, Aura component, and Visualforce page you write or review.
请将以下检查项应用于你编写或审核的每一个LWC、Aura组件和Visualforce页面。

Section 1 — LWC Quality Standards

第1节 — LWC质量标准

1.1 Data Access Pattern Selection

1.1 数据访问模式选择

Choose the right data access pattern before writing JavaScript controller code:
Use casePatternWhy
Read a single record reactively (follows navigation)
@wire(getRecord, { recordId, fields })
Lightning Data Service — cached, reactive
Standard CRUD form for a single object
<lightning-record-form>
or
<lightning-record-edit-form>
Built-in FLS, CRUD, and accessibility
Complex server query or filtered list
@wire(apexMethodName, { param })
on a
cacheable=true
method
Allows caching; wire re-fires on param change
User-triggered action, DML, or non-cacheable server callImperative
apexMethodName(params).then(...).catch(...)
Required for DML — wired methods cannot be
@AuraEnabled
without
cacheable=true
Cross-component communication (no shared parent)Lightning Message Service (LMS)Decoupled, works across DOM boundaries
Multi-object graph relationshipsGraphQL
@wire(gql, { query, variables })
Single round-trip for complex related data
在编写JavaScript控制器代码前选择合适的数据访问模式:
用例模式原因
响应式读取单条记录(跟随导航)
@wire(getRecord, { recordId, fields })
Lightning Data Service — 已缓存、响应式
单对象的标准CRUD表单
<lightning-record-form>
<lightning-record-edit-form>
内置FLS、CRUD和可访问性支持
复杂服务端查询或过滤列表
cacheable=true
的方法上使用
@wire(apexMethodName, { param })
支持缓存;参数变化时wire会自动重新触发
用户触发的操作、DML或不可缓存的服务端调用命令式调用
apexMethodName(params).then(...).catch(...)
DML必须使用该方式 — 没有设置
cacheable=true
的wired方法不能标注
@AuraEnabled
无共享父组件的跨组件通信Lightning Message Service (LMS)解耦,可跨DOM边界工作
多对象图关系GraphQL
@wire(gql, { query, variables })
复杂关联数据仅需一次往返请求

1.2 Security Rules

1.2 安全规则

RuleEnforcement
No raw user data in
innerHTML
Use
{expression}
binding in the template — the framework auto-escapes. Never use
this.template.querySelector('.el').innerHTML = userValue
Apex
@AuraEnabled
methods enforce CRUD/FLS
Use
WITH USER_MODE
in SOQL or explicit
Schema.sObjectType
checks
No hardcoded org-specific IDs in component JavaScriptQuery or pass as a prop — never embed record IDs in source
@api
properties from parent: validate before use
A parent can pass anything — validate type and range before using as a query parameter
规则实施方式
不可在
innerHTML
中插入原始用户数据
在模板中使用
{expression}
绑定 — 框架会自动转义。绝对不要编写
this.template.querySelector('.el').innerHTML = userValue
这类代码
Apex
@AuraEnabled
方法需强制校验CRUD/FLS
在SOQL中使用
WITH USER_MODE
或显式执行
Schema.sObjectType
校验
组件JavaScript中不可硬编码特定组织的ID通过查询获取或作为属性传入 — 绝对不要在源码中嵌入记录ID
来自父组件的
@api
属性:使用前需校验
父组件可能传入任意值 — 在作为查询参数使用前需校验类型和取值范围

1.3 SLDS 2 and Styling Standards

1.3 SLDS 2与样式标准

  • Never hardcode colours:
    color: #FF3366
    → use
    color: var(--slds-c-button-brand-color-background)
    or a semantic SLDS token.
  • Never override SLDS classes with
    !important
    — compose with custom CSS properties.
  • Use
    <lightning-*>
    base components wherever they exist:
    lightning-button
    ,
    lightning-input
    ,
    lightning-datatable
    ,
    lightning-card
    , etc.
  • Base components include built-in SLDS 2, dark mode, and accessibility — avoid reimplementing their behaviour.
  • If using custom CSS, test in both light mode and dark mode before declaring done.
  • 绝对不要硬编码颜色:
    color: #FF3366
    → 改用
    color: var(--slds-c-button-brand-color-background)
    或语义化SLDS令牌。
  • 绝对不要
    !important
    覆盖SLDS类 — 通过自定义CSS属性组合实现。
  • 只要存在对应的
    <lightning-*>
    基础组件就优先使用:
    lightning-button
    lightning-input
    lightning-datatable
    lightning-card
    等。
  • 基础组件内置SLDS 2、暗黑模式和可访问性支持 — 不要重复实现其功能。
  • 如果使用自定义CSS,交付前请同时在亮色模式暗黑模式下测试。

1.4 Accessibility Requirements (WCAG 2.1 AA)

1.4 可访问性要求(WCAG 2.1 AA)

Every LWC component must pass all of these before it is considered done:
  • All form inputs have
    <label>
    or
    aria-label
    — never use placeholder as the only label
  • All icon-only buttons have
    alternative-text
    or
    aria-label
    describing the action
  • All interactive elements are reachable and operable by keyboard (Tab, Enter, Space, Escape)
  • Colour is not the only means of conveying status — pair with text, icon, or
    aria-*
    attributes
  • Error messages are associated with their input via
    aria-describedby
  • Focus management is correct in modals — focus moves into the modal on open and back on close
每个LWC组件必须满足以下所有要求才可视为开发完成:
  • 所有表单输入都有
    <label>
    aria-label
    — 绝对不要仅用placeholder作为唯一标签
  • 所有仅显示图标的按钮都有
    alternative-text
    aria-label
    描述其操作
  • 所有可交互元素都可通过键盘访问和操作(Tab、Enter、Space、Escape)
  • 颜色不是传递状态的唯一方式 — 需搭配文本、图标或
    aria-*
    属性
  • 错误信息通过
    aria-describedby
    与对应的输入项关联
  • 弹窗的焦点管理正确 — 弹窗打开时焦点移入,关闭时焦点回到原位置

1.5 Component Communication Rules

1.5 组件通信规则

DirectionMechanism
Parent → Child
@api
property or calling a
@api
method
Child → Parent
CustomEvent
this.dispatchEvent(new CustomEvent('eventname', { detail: data }))
Sibling / unrelated componentsLightning Message Service (LMS)
Never use
document.querySelector
,
window.*
, or Pub/Sub libraries
For Flow screen components:
  • Events that need to reach the Flow runtime must set
    bubbles: true
    and
    composed: true
    .
  • Expose
    @api value
    for two-way binding with the Flow variable.
通信方向机制
父 → 子
@api
属性或调用
@api
方法
子 → 父
CustomEvent
this.dispatchEvent(new CustomEvent('eventname', { detail: data }))
兄弟/无关联组件Lightning Message Service (LMS)
禁止使用
document.querySelector
window.*
或 Pub/Sub类库
针对Flow屏幕组件:
  • 需要传递到Flow运行时的事件必须设置
    bubbles: true
    composed: true
  • 暴露
    @api value
    用于和Flow变量实现双向绑定。

1.6 JavaScript Performance Rules

1.6 JavaScript性能规则

  • No side effects in
    connectedCallback
    : it runs on every DOM attach — avoid DML, heavy computation, or rendering state mutations here.
  • Guard
    renderedCallback
    : always use a boolean guard to prevent infinite render loops.
  • Avoid reactive property traps: setting a reactive property inside
    renderedCallback
    causes a re-render — use it only when necessary and guarded.
  • Do not store large datasets in component state — paginate or stream large results instead.
  • connectedCallback
    中不要写副作用逻辑
    :它会在每次DOM挂载时运行 — 避免在此处执行DML、 heavy computation或渲染状态变更。
  • renderedCallback
    需添加守卫
    :始终使用布尔值守卫避免无限渲染循环。
  • 避免响应式属性陷阱:在
    renderedCallback
    中设置响应式属性会触发重渲染 — 仅在必要时使用且必须添加守卫。
  • 不要在组件状态中存储大型数据集 — 改用分页或流式返回大型结果。

1.7 Jest Test Requirements

1.7 Jest测试要求

Every component that handles user interaction or retrieves Apex data must have a Jest test:
javascript
// Minimum test coverage expectations
it('renders the component with correct title', async () => { ... });
it('calls apex method and displays results', async () => { ... });  // Wire mock
it('dispatches event when button is clicked', async () => { ... });
it('shows error state when apex call fails', async () => { ... }); // Error path
Use
@salesforce/sfdx-lwc-jest
mocking utilities:
  • wire
    adapter mocking:
    setImmediate
    +
    emit({ data, error })
  • Apex method mocking:
    jest.mock('@salesforce/apex/MyClass.myMethod', ...)

每个处理用户交互或拉取Apex数据的组件都必须编写Jest测试:
javascript
// 最低测试覆盖率要求
it('renders the component with correct title', async () => { ... });
it('calls apex method and displays results', async () => { ... });  // Wire mock
it('dispatches event when button is clicked', async () => { ... });
it('shows error state when apex call fails', async () => { ... }); // Error path
使用
@salesforce/sfdx-lwc-jest
模拟工具:
  • wire
    适配器模拟:
    setImmediate
    +
    emit({ data, error })
  • Apex方法模拟:
    jest.mock('@salesforce/apex/MyClass.myMethod', ...)

Section 2 — Aura Component Standards

第2节 — Aura组件标准

2.1 When to Use Aura vs LWC

2.1 何时使用Aura vs LWC

  • New components: always LWC unless the target context is Aura-only (e.g. extending
    force:appPage
    , using Aura-specific events in a legacy managed package).
  • Migrating Aura to LWC: prefer LWC, migrate component-by-component; LWC can be embedded inside Aura components.
  • 新组件优先选择LWC,除非目标运行上下文仅支持Aura(例如扩展
    force:appPage
    、在旧托管包中使用Aura专属事件)。
  • 将Aura迁移到LWC:优先使用LWC,按组件逐步迁移;LWC可以嵌入到Aura组件中。

2.2 Aura Security Rules

2.2 Aura安全规则

  • @AuraEnabled
    controller methods must declare
    with sharing
    and enforce CRUD/FLS — Aura does not enforce them automatically.
  • Never use
    {!v.something}
    with unescaped user data in
    <div>
    unbound helpers — use
    <ui:outputText value="{!v.text}" />
    or
    <c:something>
    to escape.
  • Validate all inputs from component attributes before using them in SOQL / Apex logic.
  • @AuraEnabled
    控制器方法必须声明
    with sharing
    并强制校验CRUD/FLS — Aura不会自动执行这些校验。
  • 不要在
    <div>
    非绑定辅助工具中使用包含未转义用户数据的
    {!v.something}
    — 使用
    <ui:outputText value="{!v.text}" />
    <c:something>
    进行转义。
  • 在SOQL/Apex逻辑中使用组件属性的输入值前必须先校验。

2.3 Aura Event Design

2.3 Aura事件设计

  • Component events for parent-child communication — lowest scope.
  • Application events only when component events cannot reach the target — they broadcast to the entire app and can be a performance and maintenance problem.
  • For hybrid LWC + Aura stacks: use Lightning Message Service to decouple communication — do not rely on Aura application events reaching LWC components.

  • 父子通信用组件事件 — 作用域最小。
  • 仅当组件事件无法触达目标时才使用应用事件 — 应用事件会向整个应用广播,可能引发性能和维护问题。
  • 针对LWC + Aura混合栈:使用Lightning Message Service解耦通信 — 不要依赖Aura应用事件触达LWC组件。

Section 3 — Visualforce Security Standards

第3节 — Visualforce安全标准

3.1 XSS Prevention

3.1 XSS防护

xml
<!-- ❌ NEVER — renders raw user input as HTML -->
<apex:outputText value="{!userInput}" escape="false" />

<!-- ✅ ALWAYS — auto-escaping on -->
<apex:outputText value="{!userInput}" />
<!-- Default escape="true" — platform HTML-encodes the output -->
Rule:
escape="false"
is never acceptable for user-controlled data. If rich text must be rendered, sanitise server-side with a whitelist before output.
xml
<!-- ❌ 禁止 — 将原始用户输入渲染为HTML -->
<apex:outputText value="{!userInput}" escape="false" />

<!-- ✅ 推荐 — 开启自动转义 -->
<apex:outputText value="{!userInput}" />
<!-- 默认escape="true" — 平台会对输出进行HTML编码 -->
规则:用户可控数据绝对不允许设置
escape="false"
。如果必须渲染富文本,输出前需在服务端使用白名单进行清理。

3.2 CSRF Protection

3.2 CSRF防护

Use
<apex:form>
for all postback actions — the platform injects a CSRF token automatically into the form. Do not use raw
<form method="POST">
HTML elements, which bypass CSRF protection.
所有回发操作都使用
<apex:form>
— 平台会自动向表单中注入CSRF令牌。不要使用原生
<form method="POST">
HTML元素,这类元素会绕过CSRF防护。

3.3 SOQL Injection Prevention in Controllers

3.3 控制器中SOQL注入防护

apex
// ❌ NEVER
String soql = 'SELECT Id FROM Account WHERE Name = \'' + ApexPages.currentPage().getParameters().get('name') + '\'';
List<Account> results = Database.query(soql);

// ✅ ALWAYS — bind variable
String nameParam = ApexPages.currentPage().getParameters().get('name');
List<Account> results = [SELECT Id FROM Account WHERE Name = :nameParam];
apex
// ❌ 禁止
String soql = 'SELECT Id FROM Account WHERE Name = \\'' + ApexPages.currentPage().getParameters().get('name') + '\\'';
List<Account> results = Database.query(soql);

// ✅ 推荐 — 绑定变量
String nameParam = ApexPages.currentPage().getParameters().get('name');
List<Account> results = [SELECT Id FROM Account WHERE Name = :nameParam];

3.4 View State Management Checklist

3.4 视图状态管理检查清单

  • View state is under 135 KB (check in browser developer tools or the Salesforce View State tab)
  • Fields used only for server-side calculations are declared
    transient
  • Large collections are not persisted across postbacks unnecessarily
  • readonly="true"
    is set on
    <apex:page>
    for read-only pages to skip view-state serialisation
  • 视图状态小于135 KB(可在浏览器开发者工具或Salesforce视图状态标签页中检查)
  • 仅用于服务端计算的字段声明为
    transient
  • 大型集合不会不必要地在回发间持久化
  • 只读页面的
    <apex:page>
    设置
    readonly="true"
    以跳过视图状态序列化

3.5 FLS / CRUD in Visualforce Controllers

3.5 Visualforce控制器中的FLS/CRUD校验

apex
// Before reading a field
if (!Schema.sObjectType.Account.fields.Revenue__c.isAccessible()) {
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'You do not have access to this field.'));
    return null;
}

// Before performing DML
if (!Schema.sObjectType.Account.isDeletable()) {
    throw new System.NoAccessException();
}
Standard controllers enforce FLS for bound fields automatically. Custom controllers do not — FLS must be enforced manually.

apex
// 读取字段前
if (!Schema.sObjectType.Account.fields.Revenue__c.isAccessible()) {
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, '你没有权限访问该字段。'));
    return null;
}

// 执行DML前
if (!Schema.sObjectType.Account.isDeletable()) {
    throw new System.NoAccessException();
}
标准控制器会自动为绑定字段执行FLS校验,自定义控制器不会 — 必须手动强制校验FLS。

Quick Reference — Component Anti-Patterns Summary

快速参考 — 组件反模式汇总

Anti-patternTechnologyRiskFix
innerHTML
with user data
LWCXSSUse template bindings
{expression}
Hardcoded hex coloursLWC/AuraDark-mode / SLDS 2 breakUse SLDS CSS custom properties
Missing
aria-label
on icon buttons
LWC/Aura/VFAccessibility failureAdd
alternative-text
or
aria-label
No guard in
renderedCallback
LWCInfinite rerender loopAdd
hasRendered
boolean guard
Application event for parent-childAuraUnnecessary broadcast scopeUse component event instead
escape="false"
on user data
VisualforceXSSRemove — use default escaping
Raw
<form>
postback
VisualforceCSRF vulnerabilityUse
<apex:form>
No
with sharing
on custom controller
VF / ApexData exposureAdd
with sharing
declaration
FLS not checked in custom controllerVF / ApexPrivilege escalationAdd
Schema.sObjectType
checks
SOQL concatenated with URL paramVF / ApexSOQL injectionUse bind variables
反模式技术栈风险修复方案
对用户数据使用
innerHTML
LWCXSS使用模板绑定
{expression}
硬编码十六进制颜色LWC/Aura暗黑模式/SLDS 2样式异常使用SLDS CSS自定义属性
图标按钮缺失
aria-label
LWC/Aura/VF可访问性不达标添加
alternative-text
aria-label
renderedCallback
中无守卫
LWC无限重渲染循环添加
hasRendered
布尔值守卫
父子通信用应用事件Aura不必要的广播作用域改用组件事件
对用户数据设置
escape="false"
VisualforceXSS移除该配置,使用默认转义
原生
<form>
回发
VisualforceCSRF漏洞使用
<apex:form>
自定义控制器未添加
with sharing
VF/Apex数据泄露添加
with sharing
声明
自定义控制器未校验FLSVF/Apex权限提升添加
Schema.sObjectType
校验
SOQL与URL参数直接拼接VF/ApexSOQL注入使用绑定变量
",