【必読】なぜ今、Aura ではなく LWR を選ぶべきなのか?

LWRにおけるサードパーティスクリプトの正しい読み込み方:x-oasis-script の活用とExperience Delivery (SSR) 時代の注意点

この記事はバージョン Spring ’26 において執筆しています。
現在の動作と異なる場合がありますので、ご認識おきください。

なぜLWRで外部スクリプトが動かないのか

Lightning Web Runtime (LWR) を採用したExperience Cloudサイト(特にBuild Your Ownテンプレート)で、開発者が一度は直面する「壁」があります。それは、「Google MapsやStripe、チャットボットなどの外部スクリプトを読み込んでも、コンポーネントを操作できない」という問題です。

従来のAuraサイトとは異なり、LWRはWeb標準のShadow DOMを厳格に適用しています。通常の <script> タグで読み込まれたライブラリは、ページ全体(Document)のコンテキストには存在しますが、各LWCコンポーネントが持つ「カプセル化されたShadow Tree」の内部にはアクセス権限を持ちません。

結果として、外部ライブラリが document.getElementById('my-map') のようにDOMを検索しても、その要素はShadow DOMの壁の向こう側にあり、「要素が見つかりません」というエラーで沈黙することになります。

この記事では、この問題を解決するSalesforce固有のアプローチである <x-oasis-script> と、Spring ’26以降の Experience Delivery (SSR) 環境で開発者が陥りやすい「致命的な落とし穴」について、アーキテクチャ視点で解説します。

解決策:<x-oasis-script> (Privileged Script Tag) とは

このShadow DOMの制約を打破するために用意されているのが、特権スクリプトタグ <x-oasis-script> です。

このタグを使用して外部スクリプトを読み込むと、そのスクリプトはLWCのShadow DOM境界をバイパスする特権を持ちます。つまり、コンポーネント内部の要素であっても、まるでLight DOMにあるかのようにアクセスが可能になります。

実装は非常にシンプルです。LWCのテンプレート(HTML)内で、通常の script タグの代わりに使用します。

コード例 1: HTMLでの基本実装

<template>
    <x-oasis-script 
        src="https://www.example.com/libs/example-library.js"
        onload={handleScriptLoad}>
    </x-oasis-script>

    <div class="map-container" lwc:dom="manual"></div>
</template>

これにより、クライアントサイドレンダリング(CSR)の環境下では、外部スクリプトの問題は概ね解決します。しかし、話はここで終わりません。これから普及が進むExperience Delivery環境では、新たな考慮が必要です。

【最重要】Experience Delivery (SSR) 環境での致命的な落とし穴

ここが本記事の核心です。Spring ’26以降、LWRサイトのパフォーマンスを劇的に向上させる Experience Delivery (SSR: Server-Side Rendering) の導入が進みます。

SSR環境では、LWCコンポーネントは最初にNode.jsベースのサーバー上で実行され、HTML文字列としてクライアントに返されます。ここで重大な問題が発生します。

SSRにおける技術的制約

  1. ブラウザAPIの欠如: サーバー環境には windowdocument オブジェクトが存在しません。
  2. ライフサイクルの罠: constructorconnectedCallback はサーバーサイドでも実行されます。ここで不用意に外部スクリプト(通常 window オブジェクトに依存している)を参照すると、サーバーエラーが発生し、ページのレンダリング自体が失敗します。

experience_delivery.pdf』の仕様に基づき、SSR環境でコンポーネントを「ポータブル(移植可能)」に保つためのガード処理が必須となります。

コード例 2: SSRセーフなJavaScript実装

ブラウザAPIに依存する外部ライブラリや処理は、import.meta.env.SSR フラグを使用してサーバー実行時から除外する必要があります。また、非ポータブルなモジュールの読み込みには動的インポート(Dynamic Import)を活用します。

// myComponent.js
import { LightningElement } from 'lwc';

export default class MyComponent extends LightningElement {
    
    isLibLoaded = false;

    // SSR環境(サーバー)でもクライアントでも実行されるライフサイクル
    async connectedCallback() {
        // 重要: サーバーサイド実行時はブラウザAPI依存コードをガードする
        if (import.meta.env.SSR) {
            // サーバーサイドでの処理(ログ出力など安全な処理のみ)
            console.log('Server side rendering: Skipping external script logic.');
            return;
        }

        // ここから下はクライアントサイド(ブラウザ)でのみ実行される
        try {
            // ブラウザ環境でのみ必要な非ポータブルなモジュールを動的にインポートする
            // ※これはSSRでのビルドエラーを防ぐための重要なテクニックです
            const { someBrowserSpecificFeature } = await import('some/browser-library');
            someBrowserSpecificFeature();
        } catch (error) {
            console.error('Dynamic import failed:', error);
        }
    }

    // ヒント: renderedCallback はクライアントサイドでのみ実行されるため
    // DOM操作やwindowオブジェクトへのアクセスはここに書くのが最も安全です
    renderedCallback() {
        if (!this.isLibLoaded) {
            this.checkExternalScript();
        }
    }

    checkExternalScript() {
        // windowオブジェクトへのアクセスも安全
        if (window.ExampleLibrary) {
            this.isLibLoaded = true;
            this.initializeMap();
        }
    }

    initializeMap() {
        // DOM操作ロジック...
    }
}

この「SSRガード」を忘れると、ローカルプレビューでは動くのに、本番のExperience Delivery環境にデプロイした瞬間にページがクラッシュする、という事態に陥ります。

実装のベストプラクティス:LWCと外部スクリプトの連携

外部スクリプトは非同期で読み込まれるため、LWC側では「いつスクリプトが利用可能になったか」を検知する仕組みが必要です。

ロード検知と初期化フロー

  1. onload イベント: <x-oasis-script>onload ハンドラを利用します。
  2. グローバルオブジェクトの監視: スクリプトによっては onload が発火しても初期化が完了していない場合があります(例: Stripeなど)。その場合、window オブジェクトをチェックします。

コード例 3: 堅牢な初期化ロジック

// myComponent.js (続き)

    handleScriptLoad() {
        // スクリプトタグのロード完了イベント
        console.log('Script tag loaded.');
        
        // SSRガードを入れた上で初期化を試みる
        if (!import.meta.env.SSR) {
            this.initializeLibrary();
        }
    }

    initializeLibrary() {
        // ライブラリがwindowオブジェクトにアタッチされているか最終確認
        // (TypeScriptを使用している場合は window['MyLib'] のようにアクセス)
        if (typeof window.MyLib !== 'undefined') {
            const container = this.template.querySelector('.map-container');
            
            // ライブラリを使用してDOMを描画
            window.MyLib.createMap(container, {
                zoom: 10,
                center: { lat: 35.6895, lng: 139.6917 }
            });
        } else {
            // ロードタイミングのズレを考慮し、少し待って再試行するなどのフォールバック
            setTimeout(() => this.initializeLibrary(), 100);
        }
    }

セキュリティ設定:CSP (Content Security Policy) の壁

技術的に正しいコードを書いても、Salesforceのセキュリティ設定でブロックされれば動作しません。特にLWRサイトは厳格な CSP (Content Security Policy) が適用されます。

  1. 信頼済みURLの登録: [設定] > [信頼済み URL] で、外部スクリプトのドメイン(例: *.googleapis.com)を登録し、「スクリプト」の許可を与える必要があります。
  2. CSPモードの選択: Salesforce Spring ’26 Release Notes にある通り、CSP違反の影響を確認しやすくなっています。厳格なCSP(Strict CSP)で動作しないレガシーなライブラリを使用する場合は、LWRの設定で「Relaxed CSP(緩和されたCSP)」の使用を検討するか、ライブラリ側がNonce(ナンス)に対応しているかを確認してください。

まとめ:System of ActionとしてのLWRサイト

LWRサイトにおける外部スクリプトの扱いは、単なる「表示」の問題ではありません。 Shadow DOMを正しくバイパスし、SSR環境でも落ちない堅牢なコードを実装することで、LWRサイトは決済、地図連携、対話型AIなどを備えた「System of Action(行動するシステム)」へと進化します。

Experience Deliveryの導入により、パフォーマンス(Core Web Vitals)は向上しますが、その分開発者には「サーバーサイドでの実行」を意識したコーディングが求められます。import.meta.env.SSR を活用し、次世代の高速なエクスペリエンス構築に備えてください。

参考URL

Salesforce Spring ’26 Release Notes

Improve LWR Site Performance with Experience Delivery

DXforceの管理人

福島 瑛二

2013年にJavaエンジニアとしてのキャリアをスタート。2019年にSalesforceと出会い、Salesforceエンジニアの道へ。

デザインや UI/UX の観点からもシステムを捉え、ユーザーにとって心地よい体験を実装することにやりがいを感じています。

CRM(顧客データ)や Data Cloud と連携した高度なサイトを目に見える形で表現できる Experience Cloud に大きな可能性を見出しており、バックエンドのデータ構造とフロントエンドの表現力を極めることがこれからの Salesforce エンジニアに求められるスキルだと確信しています。

Trailblazer: efukushima

福島 瑛二をフォローする

読者の声

タイトルとURLをコピーしました