sf-lwc について
sf-lwcは、Salesforce開発者向けのLightning Web Componentsスキャフォールディングスキルです。コンポーネントファイルや設定、Jestテストを生成し、SLDSスタイル、データ取得、ナビゲーション、メッセージングのパターンも扱います。
主な機能
- 01HTML、JS、CSS、メタデータファイルを生成
- 02モックデータを使ったJestテストを作成
- 03SLDSスタイルの指針を提示
- 04LDSとApexのデータパターンを採用
- 05ナビゲーションとLightning Message Serviceを扱う
活用シーン
- 01新しいLWCバンドルを作成
- 02コンポーネントのテストを追加
- 03レコードCRUD画面を構築
- 04DOMをまたぐコンポーネント通信を実装
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ clientell-ai/ salesforce-skills --skill sf-lwcインストール後、Claude Code で/sf-lwcを入力して読み込まれたか確認します。
同梱ファイルreferences/lwc-patterns.md
詳細
LWC スキャフォルダー
Salesforce Lightning Web Component のスペシャリストとして、完全で本番環境に対応した LWC バンドルを生成します。
LWC バンドルの構成
すべての LWC は、force-app/main/default/lwc/componentName/ にある以下のファイルで構成されます。
myComponent/
├── myComponent.html # Template
├── myComponent.js # Controller
├── myComponent.css # Styles (SLDS-compliant)
├── myComponent.js-meta.xml # Configuration
└── __tests__/
└── myComponent.test.js # Jest tests
命名規則
- バンドルフォルダー:
camelCase(例:accountList) - HTML マークアップ:
c-名前空間を付けたkebab-case(例:<c-account-list>) - JS クラス:
PascalCase(例:AccountList) - CSS:コンポーネント名に準拠
JavaScript コントローラーパターン
import { LightningElement, api, wire, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import getRecords from '@salesforce/apex/MyController.getRecords';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
export default class MyComponent extends NavigationMixin(LightningElement) {
@api recordId;
@track records = [];
error;
isLoading = false;
@wire(getRecords, { recordId: '$recordId' })
wiredRecords({ error, data }) {
if (data) {
this.records = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.records = [];
}
}
handleAction() {
this.isLoading = true;
imperativeMethod({ param: this.recordId })
.then(result => {
this.dispatchEvent(new ShowToastEvent({
title: 'Success',
message: 'Operation completed',
variant: 'success'
}));
})
.catch(error => {
this.dispatchEvent(new ShowToastEvent({
title: 'Error',
message: error.body?.message || 'An error occurred',
variant: 'error'
}));
})
.finally(() => {
this.isLoading = false;
});
}
}
Meta XML 設定
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>62.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<objects>
<object>Account</object>
</objects>
<property name="title" type="String" default="My Component"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Jest テストパターン
import { createElement } from 'lwc';
import MyComponent from 'c/myComponent';
import getRecords from '@salesforce/apex/MyController.getRecords';
// Mock Apex method
jest.mock('@salesforce/apex/MyController.getRecords', () => ({
default: jest.fn()
}), { virtual: true });
const MOCK_DATA = [
{ Id: '001xx000003ABCDEF', Name: 'Test Account' }
];
describe('c-my-component', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
it('renders records when data is returned', async () => {
getRecords.mockResolvedValue(MOCK_DATA);
const element = createElement('c-my-component', { is: MyComponent });
element.recordId = '001xx000003ABCDEF';
document.body.appendChild(element);
await Promise.resolve();
const items = element.shadowRoot.querySelectorAll('.record-item');
expect(items.length).toBe(1);
});
it('shows error when apex call fails', async () => {
getRecords.mockRejectedValue(new Error('Test error'));
const element = createElement('c-my-component', { is: MyComponent });
document.body.appendChild(element);
await Promise.resolve();
const errorEl = element.shadowRoot.querySelector('.error-message');
expect(errorEl).toBeTruthy();
});
});
Lightning Data Service(LDS)
Apex を使わずに CRUD を行うには、lightning/uiRecordApi を使用します。
getRecordワイヤーアダプター — 項目レベルセキュリティを適用してレコードを読み取りますcreateRecord、updateRecord、deleteRecord— 命令型の CRUD 操作ですgetObjectInfo、getPicklistValues— メタデータにアクセスしますrefreshApex()— 更新後にワイヤーキャッシュを無効化します- 使用する場面:シンプルな CRUD。複雑なクエリやビジネスロジックには、Apex ワイヤーを使用します。
ライフサイクルフック
| フック | タイミング | 一般的な用途 |
|---|---|---|
constructor() | コンポーネントの作成時 | 状態の初期化 |
connectedCallback() | DOM への挿入時 | データの取得、リスナーの追加 |
renderedCallback() | 各レンダリングの後 | DOM 操作(フラグでガードしてください!) |
disconnectedCallback() | DOM からの削除時 | リスナーのクリーンアップ、LMS の購読解除 |
errorCallback(error, stack) | 子コンポーネントのエラー発生時 | エラーバウンダリー、ロギング |
ナビゲーション
ページ参照タイプには NavigationMixin を使用します。
standard__recordPage— レコードの表示/編集/複製(recordId、actionNameが必要です)standard__objectPage— オブジェクトのホーム/リスト/新規ページ(objectApiName、actionNameが必要です)standard__namedPage— 標準ページ(home、chatter、filePreview)standard__webPage— 外部 URL(urlが必要です)
Lightning Message Service(LMS)
LWC、Aura、Visualforce 間の DOM をまたぐ通信です。
.messageChannel-meta.xmlでメッセージチャネルを定義しますpublish(messageContext, channel, payload)で送信しますsubscribe(messageContext, channel, handler, {scope: APPLICATION_SCOPE})で受信します- メモリリークを防ぐため、必ず
disconnectedCallback()でunsubscribe()を呼び出します
Shadow DOM と Light DOM
- Shadow DOM(デフォルト):CSS の分離、DOM のカプセル化 — ほとんどのコンポーネントで使用します
- Light DOM(
lwc:dom="light"):カプセル化なし — コンポーネント間の ARIA 参照、グローバル CSS、サードパーティライブラリからの DOM アクセスが必要な場合に使用します - Shadow DOM は外部からの
document.querySelector()をブロックします — 内部ではthis.template.querySelector()を使用します
ルール
- スタイリングには常に SLDS クラスを使用します — SLDS にユーティリティがある場合は、カスタム CSS を避けます
- 公開プロパティには
@apiを使用します。プロパティはデフォルトでリアクティブです - 宣言的なデータ取得には
@wireを使用します - ユーザーが開始するアクションには、命令型の Apex 呼び出しを使用します
- すべてのコンポーネントでローディング状態とエラーを処理します
- シンプルな CRUD には
lightning-record-form/lightning-record-edit-formを使用します - 子から親への通信にはカスタムイベントをディスパッチします
- DOM をまたぐ通信には
MessageChannelを使用します
注意点
@trackは非推奨です — API v40 以降では、すべてのプロパティがデフォルトでリアクティブですrenderedCallback()はレンダリングのたびに実行されます — 無限ループを防ぐため、常にブール値フラグでガードしてください- LDS キャッシュは自動では更新されません — 命令型の更新後に
refreshApex(wiredProperty)を呼び出します - メモリリークを防ぐため、LMS の購読は
disconnectedCallback()で必ず解除する必要があります - Shadow DOM はコンポーネント間の ID ベースの ARIA 参照(
aria-labelledby)をブロックします — アクセシビリティのためには Light DOM を使用します - CSP は
eval()、new Function()、インラインの<script>をブロックします — サードパーティライブラリは Static Resources からloadScript()を使って読み込みます @apiプロパティはコンポーネント内で読み取り専用です — 親が値を設定し、子は変更できません- リアクティブなパラメーターが変わるとワイヤーアダプターが再実行されます — 不必要なパラメーターの変更は避けます
ワークフロー
- コンポーネントの要件を把握します
- 拡張できる既存のコンポーネントがないか確認します
- バンドルファイル一式(HTML、JS、CSS、meta.xml)を生成します
- モックデータを使って Jest テストファイルを生成します
- デプロイします:
sf project deploy start -d force-app/main/default/lwc/componentName/
参考資料
- LWC パターン — LDS、ナビゲーション、LMS、データテーブル、カスタムイベント、スロット、アクセシビリティ、SLDS、サードパーティーライブラリ、動的コンポーネント、Experience Cloud
コメント
Markdown 対応