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

【カーコンフィギュレーションサイト 制作日誌 Vol.2】Three.jsを独自ビルドし外部サーバーの3DモデルをLWCで読み込む

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

Experience Cloud上に3Dカーコンフィギュレーターを実装する際、フロントエンドの描画エンジンとしてThree.jsを採用するのが一般的です。しかし、SalesforceのLWC環境にそのままThree.jsを導入しようとすると、プラットフォーム特有のモジュール仕様、ファイル容量制限、そして厳格なセキュリティポリシーという3つの壁に直面します。

本記事では、連載第2回として、これらの制約を越えて外部の高品質な3Dモデルを安全かつ確実に読み込むためのアーキテクチャと実装手法を解説します。

DXforce Point for Developers

今回の記事で作成したコンポーネントと.glbファイル(ZIP)をGitHubで公開しています。 ぜひ Clone して、あなたの組織で動かしてみてください。


LWRって何?どんなメリットがあるの?
そんな疑問を解決するにはまずは以下の記事をご覧ください。
Salesforce LWRとは? Experience Cloudの次世代ランタイムを徹底解説

LWCの仕様制約と外部リソース読み込みの要件


Salesforce環境でThree.jsと外部アセットを扱う場合、以下の仕様制約を前提とした設計が要求されます。

ES Modules(ESM)の非対応と独自ビルド

  • 課題: LWCで外部スクリプトを非同期に読み込む loadScript APIは、ESM(import/export 構文)をネイティブにサポートしていません。最新のThree.jsやそのアドオン(GLTFLoaderOrbitControls)はESMで提供されているため、そのまま静的リソースに配置してもロード時に構文エラーとなります。
  • 解決策: ローカルのNode.js環境でモジュールバンドラー(Rollup)を使用します。Three.jsのコアエンジンと必要なすべてのアドオンを、ブラウザで即時実行可能なIIFE形式の単一ファイル(three-bundle.min.js)として統合ビルドし、静的リソースにアップロードします。これにより、ロード順序の複雑化を避け、一度の loadScript 呼び出しで全機能へのアクセスが可能になります。

静的リソースの5MB制限と外部ホスティング

  • 課題: Salesforceの静的リソースは、単一ファイル(非圧縮状態)で最大5MBという厳格な容量制限があります。高品質な3Dモデル(.glbファイル)は数MB〜数十MBに達することが多く、Salesforce内部でのホスティングが困難です。
  • 解決策: 容量制限を回避するため、.glbファイルは外部サーバー(AWS / Xserver等)に配置し、LWCからはその絶対URLを指定して直接パースします。
  • 必須となるセキュリティ設定: 外部サーバーと通信を行うため、以下の設定が仕様上必須となります。
  1. Salesforce側の設定: 「設定」メニューの「信頼済み URL」に、外部サーバーのドメインを登録し、通信ブロックを解除します。
  2. 外部サーバー側の設定: アセットをホストするサーバーの .htaccessAccess-Control-Allow-Origin: *(またはSalesforce組織のドメイン)が正しく出力されるよう設定します。
# glb および .gltf ファイルに対するCORSを許可する
<FilesMatch "\.(glb|gltf)$">
    Header set Access-Control-Allow-Origin "*"
</FilesMatch>

# .glbファイルのMIMEタイプを定義する
AddType model/gltf-binary .glb

LWS環境下でのWorker生成制限(CSPエラー)

  • 課題: LWS(Lightning Web Security)が有効な環境において、外部サーバーから取得した3Dモデルをパースする際、Three.js内部で使用される ImageBitmapblob: Worker を生成しようと試みます。しかし、SalesforceのCSP設定によってはこれがブロックされ、モデルのロードが途中で失敗します。
  • 解決策: モデルロードの直前のみ window.createImageBitmap を一時的に undefined に上書きし、Three.js側のパーサーを従来の Image オブジェクトによる読み込み処理へと強制的にフォールバックさせます。

実装手順:ローカルビルドとLWCへの組み込み

Node.jsのインストール(準備)

PCにNode.jsがインストールされていない場合は、事前の準備が必要です。

  1. Node.js公式サイトにアクセスします。
  2. 「LTS(推奨版)」と記載されているインストーラーをダウンロードし、画面の指示に従ってインストールします。
  3. インストール完了後、ターミナル(Windowsの場合はコマンドプロンプトやPowerShell、Macの場合はターミナル)を開き、以下のコマンドを入力してバージョンが表示されれば成功です。
# Node.jsのバージョン確認
node -v

# npmのバージョン確認
npm -v

プロジェクトフォルダの作成と初期化

作業用のフォルダを作成し、npmの管理下に入れます。フォルダ作成はコマンドではなくGUIで実施してもOK。

# 作業フォルダを作成し、そのフォルダに移動する
mkdir lwc-threejs-build
cd lwc-threejs-build

# npmプロジェクトとして初期化(package.jsonという管理ファイルが作成される)
npm init -y

Three.jsをダウンロード

Three.js本体と、ビルドを実行するためのRollup本体およびプラグイン(拡張機能)をダウンロードします。

# Three.js本体のインストール
npm install three

# バンドラー(Rollup)とプラグインのインストール
npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-terser
DXforce Point for Developers

@rollup/plugin-node-resolve:
npmでインストールしたThree.jsのファイルをRollupが正しく見つけて読み込むためのプラグインです。
@rollup/plugin-terser:
最終的な出力ファイルのサイズを削る(圧縮する)ためのプラグインです。

ビルド設定ファイルとエントリーファイルの作成

ソースコードを格納するディレクトリ src と、各種設定を行うための空ファイルをターミナルから作成します。お使いのOSに合わせてコマンドを実行してください。

# ソースコード用ディレクトリの作成(Mac / Windows 共通)
mkdir src

# ==========================================
# 【Mac / Linux / Windows WSL の場合】
# ==========================================
touch src/three-entry.js
touch rollup.config.mjs

# ==========================================
# 【Windows PowerShell の場合】
# ==========================================
New-Item src/three-entry.js
New-Item rollup.config.mjs

この時点で、フォルダ構成は以下のようになっています。

lwc-threejs-build/
  ├─ node_modules/       # インストールしたパッケージが入るフォルダ(触らない)
  ├─ package.json        # npmの設定ファイル
  ├─ package-lock.json   # パッケージのバージョン固定ファイル(触らない)
  ├─ rollup.config.mjs   # ★新規作成: Rollupの設定ファイル
  └─ src/                # ★新規作成: 元となるソースコードを入れるフォルダ
      └─ three-entry.js  # ★新規作成: 結合の起点となるファイル


VS Codeなどのエディタを使用して、先ほど作成した2つのファイルに以下のコードを記述(ペースト)して保存します。

src/three-entry.js

import * as THREE from 'three';
// 必要な各アドオンを個別にインポートする
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';

// ESモジュールの名前空間オブジェクト(THREE)は拡張不可(Object.isExtensible = false)なため、
// スプレッド構文でコピーして新しい拡張可能なオブジェクトを作成する
const THREE_EXTENDED = { ...THREE };

// 各アドオンをプロパティとして追加する
THREE_EXTENDED.GLTFLoader = GLTFLoader;
THREE_EXTENDED.DRACOLoader = DRACOLoader;
THREE_EXTENDED.OrbitControls = OrbitControls;
THREE_EXTENDED.RoomEnvironment = RoomEnvironment;

// LWCのloadScriptで参照できるように、windowオブジェクトへ割り当てる
window.THREE = THREE_EXTENDED;

rollup.config.mjs

import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';

export default {
    input: 'src/three-entry.js',
    output: {
        // 出力先のディレクトリとファイル名
        file: 'dist/three-bundle.min.js',
        // LWCのloadScriptで読み込まれた瞬間に実行されるIIFE形式を指定
        format: 'iife',
        name: 'ThreeJSBundle'
    },
    plugins: [
        resolve(),
        terser() // 出力ファイルの最小化(圧縮)
    ]
};

package.json へのビルドコマンド追記と実行

エディタで package.json を開き、"scripts" セクションに、Rollupを実行するためのコマンド "build": "rollup -c" を追記して保存します。

{
  "scripts": {
    "build": "rollup -c"
  }
}


ターミナルに戻り、以下のコマンドを実行してビルドを開始します。

npm run build

処理が成功すると、自動的に dist ディレクトリが作成され、その中に three-bundle.min.js が生成されます。このファイルはThree.jsのコアエンジンとGLTFLoaderが統合され、ESMに依存しない形式で圧縮されています。

LWCからの読み込み実装例

生成された three-bundle.min.js を、Salesforce組織の静的リソースにアップロードします(例としてリソース名を threejsBundle とします)。
これにより、LWC側では単一ファイルの非同期読み込みのみでThree.jsを初期化することが可能になります。

import { LightningElement } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
// Rollupで独自ビルドしたThree.js統合バンドル
import THREEJS_BUNDLE from '@salesforce/resourceUrl/threejsBundle';

// 外部サーバーにホストしたモデルの絶対URL
const CAR_MODEL_URL = 'https://dxforce.site/assets/3d_model/Mclaren_final_eevee.glb';

export default class CarConfigurator extends LightningElement {
    
    // (...中略: Three.jsの初期化処理など...)

    loadCarModel() {
        // LWS環境での blob: Worker 生成エラーを回避するため、ImageBitmapの利用を一時的に無効化
        const originalCreateImageBitmap = window.createImageBitmap;
        window.createImageBitmap = undefined;
        
        const gltfLoader = new window.THREE.GLTFLoader();

        // 外部サーバーの絶対URLを直接指定してロード
        gltfLoader.load(
            CAR_MODEL_URL,
            (gltf) => {
                // ロード成功時に環境を元に戻す
                window.createImageBitmap = originalCreateImageBitmap;
                
                this.carModelGroup = gltf.scene;
                this.scene.add(this.carModelGroup);
                
                // (...中略: カラー初期設定やUI状態の更新処理...)
            },
            undefined,
            (error) => {
                // エラー時も環境を元に戻す
                window.createImageBitmap = originalCreateImageBitmap;
                console.error('外部モデルの読み込みに失敗しました。CORS設定を確認してください。', error);
            }
        );
    }
}

この構成により、Salesforceプラットフォームのファイル容量制限に縛られることなく、外部CDNや自社サーバーの大容量アセットをExperience Cloud上で柔軟に活用することが可能になります。

参考URL

Use Third-Party JavaScript Libraries (Salesforce Developers)

LWC でのサードパーティ Web コンポーネントの使用 (Beta)

Manage Trusted URLs

Installation | Three.js

ResizeObserver API (MDN Web Docs)

DXforceの管理人

福島 瑛二

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

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

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

Trailblazer: efukushima

福島 瑛二をフォローする

読者の声

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