Salesforce LWCで静的リソースを読み込む方法

Salesforce AppExchange開発でLWCに対して静的リソース（Static Resource）の読み込みに苦戦したのでその備忘録。

静的リソースを使うことは、以下の点からSalesforceが推奨している。
- CDN依存を排除し、安定した動作を確保できる
- Salesforce内部で完全管理できる

ただし、対象リソースをアップデートする場合は自前で更新する必要がある

静的リソースをLWCに組み込むには、次の3つのステップを踏む。
1. 対象の静的リソースを準備してzipする
2. LWCコンポーネントを編集する
3. リソースをデプロイする


# 1.対象の静的リソースを準備してzipする
Salesforce DXプロジェクトは、静的リソースを `staticresources` フォルダ内で管理している。
今回は例として、「Swiper」ライブラリを対象リソースとしてここに配置する。

```text
force-app/
  main/
    default/
      staticresources/
        swiper.resource-meta.xml
        swiper.zip
```

## Swiperファイルの準備
**対象リソースをzip**
zip対象のファイルは以下2つ
```
swiper-bundle.min.css
swiper-bundle.min.js
```

リソースはこちらからダウンロードできる

https://www.jsdelivr.com/package/npm/swiper?tab=files


ダウンロードしてzip名を```swiper.zip```にする

**メタデータファイルを作成**
```swiper.resource-meta.xml```し、以下のように記述する
```xml: swiper.resource-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>application/zip</contentType>
</StaticResource>
```

上記２つの準備ができたら`staticresources`に配置する


# 2.LWCコンポーネントを編集する
1で作成したリソースを読み込みたいLWCコンポーネントを以下のように編集する
例えば、読み込みたいLWCが`myLightningWebComponent`だった場合、
```html: myLightningWebComponent.html
<template>
  <lightning-card title="Swiper Example">
    <div class="swiper">
      <div class="swiper-wrapper">
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
      </div>
      <div class="swiper-pagination"></div>
    </div>
  </lightning-card>
</template>
```

```css: myLightningWebComponent.css
.swiper {
  width: 300px;
  height: 300px;
}
.swiper-slide {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #ddd;
  font-size: 24px;
  height: 300px;
}
```

と、html / css は簡易であるが、表示したいように編集する
jsは少しややこしいが、
- `@salesforce/resourceUrl/[対象リソース]`でパスを取得
- 読み込みに`loadStyle()`,`loadScript()`が必要

な点に注意する。

```js: myLightningWebComponent.js
import swiperResource from '@salesforce/resourceUrl/swiper'; // Swiperリソースのパス
import { loadStyle, loadScript } from 'lightning/platformResourceLoader';

export default class MyLightningWebComponent extends LightningElement {
    @track swiperInstance = undefined;
    swiperInitialized = false;

    renderedCallback() {
        if (!this.swiperInitialized) {
          Promise.all([
            loadStyle(this, swiperResource + '/swiper/swiper-bundle.min.css'),
            loadScript(this, swiperResource + '/swiper/swiper-bundle.min.js')
          ])
            .then(() => {
              this.initializeSwiper();
              this.swiperInitialized = true;
            })
            .catch(error => {
              console.error('Error loading Swiper resources:', error);
            });
        }
      }

      initializeSwiper() {
        const swiperContainer = this.template.querySelector('.swiper');
        if (swiperContainer) {
          this.swiperInstance = new Swiper(swiperContainer, {
            loop: false,
            effect: 'cards',
            grabCursor: true
          });
        }
      }
}

```

LWCのjsはクラスで定義されるため、リソースの状態を保持するには、インスタンスをプロパティで管理する必要がある。
また、`loadStyle()`,`loadScript()`のパスを間違えないようにする。（これを確認する方法がコンソールを追うことだけで正しいパスを掴むのに大変苦労した。。）

# 3. リソースをデプロイする
2で編集したコードを環境に反映する
```command
sfdx force:source:push
```
うまくいくとリソース反映とLWCに読み込まれる