Salesforce Lightning Experience をマスターする:開発者のための Lightning Web Components (LWC) 構築ガイド

背景と適用シナリオ

私は Salesforce 開発者です。Salesforce の世界は、Classic UI から Lightning Experience へと大きく進化しました。この移行は、単なる見た目の変化だけでなく、開発のパラダイムシフトをもたらしました。かつての Visualforce ページが主流だった時代から、Aura Components、そして現在では Lightning Web Components (LWC) (ライトニングWebコンポーネント) が、UI カスタマイズの中心的技術となっています。

Lightning Experience は、レスポンシブでインタラクティブなユーザーインターフェースを提供しますが、その真価は、ビジネスプロセスに合わせてカスタマイズできる柔軟性にあります。開発者として私たちの役割は、この強力なプラットフォーム上で、ユーザーの生産性を最大化するカスタムソリューションを構築することです。LWC は、現代の Web 標準(Web Components、ES Modules、Shadow DOM など)に基づいて構築されており、従来の Aura Components よりも優れたパフォーマンスと開発体験を提供します。

この技術記事では、Salesforce 開発者の視点から、Lightning Experience のための LWC 開発に焦点を当てます。具体的な適用シナリオとしては、以下のようなケースが挙げられます。

  • カスタムレコードページのコンポーネント: 取引先レコードページに、関連する商談のサマリーや、外部システムから取得した顧客情報を表示する。
  • 動的なフォーム: ユーザーの入力に応じて、表示される項目が動的に変化する複雑な入力フォームを作成する。
  • アプリケーションのホームページ: 特定の役割を持つユーザー向けに、タスクリスト、主要なレポート、お知らせなどを組み合わせたダッシュボードを構築する。
  • クイックアクション: ボタン一つで、複数の関連レコードを一度に作成・更新するような複雑なロジックを実行する。

本記事では、取引先レコードページに関連するすべての取引先責任者を表示するシンプルな LWC の作成を通して、LWC の基本構造、Apex との連携、そして開発におけるベストプラクティスを解説します。


原理の説明

Lightning Web Components (LWC) は、いくつかの重要な概念と技術で構成されています。これらを理解することが、効果的な LWC 開発の鍵となります。

コンポーネントバンドル (Component Bundle)

LWC は、複数のファイルから成る「バンドル」として構成されます。主要なファイルは以下の通りです。

  • HTML (.html): コンポーネントの構造を定義します。テンプレートディレクティブ (if:true, for:each など) を使用して、動的なレンダリングを行います。
  • JavaScript (.js): コンポーネントのロジックを記述します。プロパティの定義、イベント処理、サーバーとの通信などを担当します。LightningElement クラスを拡張して作成します。
  • XML (.js-meta.xml): 設定ファイルです。コンポーネントの API バージョンや、Lightning アプリケーションビルダーでどこに配置できるか (targets) などを定義します。
  • CSS (.css): コンポーネントに固有のスタイルを定義します。スタイルはコンポーネント内にスコープされ、他のコンポーネントに影響を与えません(Shadow DOM のおかげです)。

リアクティブなデータバインディング (Reactive Data Binding)

LWC はリアクティブ性を備えています。JavaScript ファイルで @track デコレータでマークされたプロパティや、プライベートプロパティの値が変更されると、そのプロパティを参照している HTML テンプレートの部分が自動的に再レンダリングされます。これにより、UI の状態管理が非常にシンプルになります。

デコレータ (Decorators)

LWC では、プロパティや関数の振る舞いを変更するために、いくつかのデコレータが提供されています。

  • @api: プロパティを公開 (public) し、親コンポーネントや Lightning アプリケーションビルダーから値を受け取れるようにします。例えば、レコードページの ID を受け取るには @api recordId; のように定義します。
  • @track: オブジェクトや配列の内部のプロパティが変更された際に、コンポーネントの再レンダリングをトリガーします。ただし、近年の LWC では、トップレベルのプロパティへの再代入でリアクティビティが確保されるため、@track の使用頻度は減少しています。
  • @wire: Salesforce のデータをリアクティブに読み込むための強力な仕組みです。Apex メソッドや Lightning Data Service のワイヤーアダプタを呼び出し、返されたデータをプロパティや関数に直接結びつけます (wire します)。データが変更されると、コンポーネントは自動的に更新されます。

Apex との連携

サーバーサイドの複雑なロジックや SOQL クエリを実行するために、LWC は Apex メソッドを呼び出すことができます。Apex メソッドは @AuraEnabled(cacheable=true) アノテーションを付与することで、LWC から呼び出し可能になります。cacheable=true を指定すると、メソッドはデータの読み取り専用となり、@wire サービスで呼び出すことができます。データの作成・更新・削除 (DML 操作) を行うメソッドは cacheable=true を指定できず、命令として (imperatively) 呼び出す必要があります。


示例代码

ここでは、取引先 (Account) レコードページに配置し、その取引先に関連するすべての取引先責任者 (Contact) を一覧表示する contactList という LWC を作成します。

1. Apex Controller: ContactController.cls

まず、指定された取引先 ID に紐づく取引先責任者リストを取得する Apex クラスを作成します。

// ContactController.cls
public with sharing class ContactController {
    // LWCから呼び出せるように @AuraEnabled アノテーションを付与
    // (cacheable=true) は、このメソッドがデータを変更せず、クライアント側でキャッシュ可能であることを示す
    // これにより @wire サービスで呼び出すことができる
    @AuraEnabled(cacheable=true)
    public static List<Contact> getContactList(String accountId) {
        // セキュリティを考慮し、WITH SECURITY_ENFORCED を使用して
        // 実行ユーザーの項目レベルセキュリティ(FLS)とオブジェクト権限を強制する
        return [
            SELECT Id, FirstName, LastName, Title, Phone, Email
            FROM Contact
            WHERE AccountId = :accountId
            WITH SECURITY_ENFORCED
            ORDER BY LastName
        ];
    }
}

2. LWC HTML Template: contactList.html

次に、取得した取引先責任者のリストを表示するための HTML テンプレートを作成します。

<!-- contactList.html -->
<template>
    <!-- Salesforce Lightning Design System (SLDS) の card コンポーネントを使用して見た目を整える -->
    <lightning-card title="Related Contacts" icon-name="standard:contact">
        <!-- コンポーネント内にパディングを追加 -->
        <div class="slds-m-around_medium">
            <!-- @wire サービスでデータが取得できた場合 -->
            <template if:true={contacts.data}>
                <!-- 取得した contacts.data 配列をループ処理する -->
                <template for:each={contacts.data} for:item="contact">
                    <!-- 各ループで一意のキーを割り当てる (パフォーマンス向上のため必須) -->
                    <p key={contact.Id}>
                        <b>{contact.FirstName} {contact.LastName}</b> ({contact.Title})
                    </p>
                </template>
            </template>

            <!-- @wire サービスでエラーが発生した場合 -->
            <template if:true={contacts.error}>
                <p>Error loading contacts. Please contact your administrator.</p>
                <!-- ここでエラーの詳細をコンソールに出力するなどの処理を追加できる -->
            </template>

            <!-- データがまだ読み込まれていない場合(ローディングスピナーなどを表示できる) -->
            <template if:false={contacts.data}>
                <template if:false={contacts.error}>
                    <p>Loading...</p>
                </template>
            </template>
        </div>
    </lightning-card>
</template>

3. LWC JavaScript: contactList.js

コンポーネントのロジックを記述します。Apex メソッドを呼び出し、結果を HTML テンプレートに渡します。

// contactList.js
import { LightningElement, api, wire } from 'lwc';
// Apex メソッドをインポート
import getContactList from '@salesforce/apex/ContactController.getContactList';

export default class ContactList extends LightningElement {
    // @api デコレータで recordId プロパティを公開する
    // これにより、コンポーネントがレコードページに配置された際に、
    // Salesforce が自動的に現在のレコードIDをこのプロパティに渡してくれる
    @api recordId;

    // @wire デコレータを使用して Apex メソッドを呼び出す
    // 第1引数: 呼び出す Apex メソッド
    // 第2引数: メソッドに渡すパラメータ。'$recordId' は recordId プロパティへのリアクティブな参照
    // recordId の値が変更されると、@wire は自動的に Apex メソッドを再実行する
    // 結果は contacts プロパティに格納される
    @wire(getContactList, { accountId: '$recordId' })
    contacts;
    
    // @wire の結果は { data, error } という形式のオブジェクトになる
    // 成功した場合: contacts.data に Apex からの戻り値が格納される
    // 失敗した場合: contacts.error にエラー情報が格納される
}

4. LWC XML Configuration: contactList.js-meta.xml

最後に、このコンポーネントを Lightning アプリケーションビルダーで利用可能にするための設定ファイルを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>58.0</apiVersion>
    <!-- isExposed を true にすることで、アプリケーションビルダーに表示される -->
    <isExposed>true</isExposed>
    <targets>
        <!-- このコンポーネントをレコードページで利用可能にする -->
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <!-- レコードページに配置された際の設定 -->
        <targetConfig targets="lightning__RecordPage">
            <!-- 取引先オブジェクトのレコードページでのみ利用可能にする(任意) -->
            <objects>
                <object>Account</object>
            </objects>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

注意事項

権限とセキュリティ (Permissions and Security)

LWC と Apex を開発する際には、Salesforce の堅牢なセキュリティモデルを常に念頭に置く必要があります。

  • Apex の with sharing: Apex クラスには with sharing を指定し、実行ユーザーの共有ルールが適用されるようにします。これにより、ユーザーがアクセス権を持たないレコードが表示されるのを防ぎます。
  • 項目レベルセキュリティ (Field-Level Security, FLS): デフォルトでは、Apex は FLS を無視して項目にアクセスします。WITH SECURITY_ENFORCED 句を SOQL クエリに追加するか、Security.stripInaccessible メソッドを使用することで、ユーザーがアクセス権を持たない項目をクエリ結果から除外できます。これは非常に重要なベストプラクティスです。
  • CRUD 権限: 同様に、オブジェクトへのアクセス権限 (Create, Read, Update, Delete) も、Apex コード内で明示的にチェックすることが推奨されます。

ガバナ制限 (Governor Limits)

Salesforce はマルチテナント環境であるため、すべての組織がリソースを公平に利用できるように Governor Limits (ガバナ制限) が設けられています。

  • SOQL クエリ: 1 つのトランザクションで発行できる SOQL クエリの数は 100 回までです。ループ内で SOQL クエリを実行することは絶対に避けるべきです。
  • DML ステートメント: 1 つのトランザクションで実行できる DML 操作 (insert, update, delete) は 150 回までです。
  • CPU 時間: 1 つのトランザクションで消費できる CPU 時間にも制限があります。 LWC から呼び出される Apex メソッドは、これらの制限を遵守するように設計する必要があります。特に、大量のデータを扱う場合は、一括処理(バルク化)を意識したコーディングが不可欠です。

エラー処理 (Error Handling)

堅牢なアプリケーションを構築するためには、適切なエラー処理が欠かせません。@wire サービスを使用する場合、結果オブジェクトの error プロパティをチェックすることで、サーバーサイドで発生したエラーを捕捉できます。命令的に Apex を呼び出す場合は、JavaScript の try...catch ブロックや Promise の .catch() を使用してエラーを処理します。ユーザーフレンドリーなエラーメッセージを表示し、開発者向けにはコンソールに詳細なエラー情報をログ出力することが推奨されます。

Locker Service

Locker Service は、LWC のセキュリティを強化するための機能です。各コンポーネントを独自のコンテナ(名前空間)で実行し、DOM へのアクセスを制限することで、他のコンポーネントやアプリケーション全体への悪影響を防ぎます。サードパーティの JavaScript ライブラリを使用する際には、Locker Service との互換性を確認する必要があります。


まとめとベストプラクティス

Lightning Experience と LWC は、Salesforce 開発者にとって強力なツールセットです。現代の Web 標準に基づいた LWC を活用することで、パフォーマンスが高く、保守性に優れたカスタム UI を効率的に構築できます。

最後に、成功のためのベストプラクティスをまとめます。

  1. Salesforce Lightning Design System (SLDS) の活用: SLDS を使用することで、Salesforce の標準 UI と一貫性のあるデザインを簡単に実現できます。カスタム CSS を書く前に、まず SLDS のユーティリティクラスやブループリントが利用できないか確認しましょう。
  2. 基本コンポーネントの再利用: lightning-card, lightning-datatable, lightning-input など、Salesforce が提供する豊富な基本コンポーネントを積極的に利用することで、開発時間を大幅に短縮できます。
  3. コンポーネントの粒度: 1つのコンポーネントに多くの機能を詰め込みすぎず、適切に責務を分割しましょう。再利用可能な小さなコンポーネントを組み合わせることで、アプリケーション全体の保守性が向上します。
  4. Apex の責務はデータアクセスに限定: Apex はビジネスロジックとデータアクセスに集中させ、UI に関するロジックは LWC の JavaScript で処理するように分離します。
  5. Salesforce DX の利用: Salesforce DX (Developer Experience) を使用して、ソース駆動開発、バージョン管理、継続的インテグレーション (CI/CD) を実践しましょう。これにより、チーム開発の効率と品質が劇的に向上します。

この記事が、皆様の Lightning Experience 開発の一助となれば幸いです。Happy Coding!

コメント