# 機能設計書 40-defineCustomElement()

## 概要

本ドキュメントは、Vue 3の`defineCustomElement()`関数の機能設計を記載する。`defineCustomElement()`はVueコンポーネントをWeb Components標準のカスタムHTML要素（Custom Element）として定義するAPIであり、Shadow DOM対応やattribute/property同期を含む包括的なWeb Components統合を提供する。

### 本機能の処理概要

`defineCustomElement()`は、Vue 3のコンポーネント定義を受け取り、`HTMLElement`を継承した`VueElement`クラスを返す。このクラスは`customElements.define()`で登録でき、通常のHTML要素のように使用できる。Shadow DOMによるスタイルカプセル化、attribute/property双方向同期、provide/inject連携、ライフサイクル管理を自動的に処理する。

**業務上の目的・背景**：Web Componentsは標準仕様のため、フレームワークに依存しないコンポーネント配布が可能である。Vueで開発したコンポーネントをReact、Angular、素のHTML等の異なる技術スタックで使用したい場合に、defineCustomElementによりVueコンポーネントをカスタム要素としてパッケージ化できる。

**機能の利用シーン**：マイクロフロントエンド、フレームワーク非依存のウィジェットライブラリ、レガシーアプリケーションへのVueコンポーネント埋め込み、CMS/WordPressプラグイン等。

**主要な処理内容**：
1. `defineComponent`によるコンポーネント定義の正規化
2. `VueElement`クラスの生成（`HTMLElement`継承）
3. Shadow DOM（`attachShadow`）の生成・管理
4. `connectedCallback`/`disconnectedCallback`によるライフサイクル管理
5. `MutationObserver`によるattribute変更の監視
6. props定義に基づくgetter/setterの動的定義
7. Number型propsの自動変換
8. `configureApp`オプションによるアプリケーションカスタマイズ
9. スタイルの注入（`_applyStyles`）
10. emit→CustomEventへの変換（`dispatchEvent`）
11. 親カスタム要素からのprovide/inject連携
12. 非同期コンポーネント（`defineAsyncComponent`）対応
13. スロットの解析と配置（shadowRoot=false時）
14. SSR対応（`defineSSRCustomElement`）

**関連システム・外部連携**：Web Components標準（Custom Elements API、Shadow DOM API）。`@vue/runtime-core`のcreateApp/defineComponent。`useShadowRoot()`/`useHost()`フック。

**権限による制御**：特になし。

## 関連画面

画面機能マッピングに該当する画面関連情報なし。

## 機能種別

コンポーネント変換 / Web Components統合

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options | `ComponentOptions \| setup function` | Yes | Vueコンポーネント定義 | - |
| extraOptions | `CustomElementOptions` | No | カスタム要素固有のオプション | - |

#### CustomElementOptions

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| styles | `string[]` | No | Shadow DOMに注入するCSSテキスト | - |
| shadowRoot | `boolean` | No | Shadow DOMを使用するか（デフォルト: true） | - |
| shadowRootOptions | `Omit<ShadowRootInit, 'mode'>` | No | Shadow Root生成オプション | - |
| nonce | `string` | No | CSPのnonce値 | - |
| configureApp | `(app: App) => void` | No | Appインスタンスのカスタマイズ関数 | - |

### 入力データソース

- Vueコンポーネント定義オブジェクトまたはsetup関数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| VueElementConstructor | `typeof VueElement` | `customElements.define()`に登録可能なクラス |

### 出力先

- Web Components Registry（`customElements.define()`経由）

## 処理フロー

### 処理シーケンス

```
defineCustomElement():
1. defineComponent()でコンポーネント定義を正規化
2. VueCustomElementクラスを生成（VueElementを継承）
3. static def = Compを設定
4. VueCustomElementを返却

connectedCallback() (DOM接続時):
1. 親VueElement検索（provide/inject用）
2. _resolved=trueかつ_instanceなし → _mount()
3. 未resolvedの場合 → _resolveDef()
   ├─ 全attribute読み取り
   ├─ MutationObserver設定
   ├─ Number型props変換
   ├─ getter/setter定義
   ├─ スタイル注入
   └─ _mount()

_mount():
1. createApp(def)でAppインスタンス生成
2. 親コンテキスト継承
3. configureApp呼び出し
4. _ceVNode設定
5. app.mount(this._root)
6. exposedプロパティのgetter定義

disconnectedCallback() (DOM切断時):
1. nextTick後に未接続を確認
2. MutationObserver切断
3. app.unmount()
4. インスタンスクリーンアップ
```

### フローチャート

```mermaid
flowchart TD
    A[defineCustomElement] --> B[defineComponent]
    B --> C[VueCustomElementクラス生成]
    C --> D[customElements.define登録]
    D --> E[DOM挿入 → connectedCallback]
    E --> F{_resolved?}
    F -->|Yes| G[_mount]
    F -->|No| H[_resolveDef]
    H --> I[attribute読み取り + MutationObserver]
    I --> J[props setter/getter定義]
    J --> K[スタイル注入]
    K --> G
    G --> L[createApp + mount]
    L --> M[exposed property定義]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | Shadow DOM制御 | shadowRoot=false以外はShadow DOMを生成。mode: 'open'で生成 | shadowRoot prop |
| BR-40-02 | Number変換 | props定義でNumberまたはtype: Numberの場合、attribute値をtoNumber()で変換 | Number型props |
| BR-40-03 | attribute同期 | _setPropでshouldReflect=trueの場合、true→空属性、string/number→文字列属性、falsy→属性除去 | shouldReflect |
| BR-40-04 | emit変換 | コンポーネントのemitをCustomEventにマッピング。ハイフネーション版も発行 | emitイベント発行時 |
| BR-40-05 | provide/inject連携 | 親VueElementのprovidesをappContext.providesのプロトタイプに設定 | 親VueElement存在時 |
| BR-40-06 | 非同期コンポーネント | __asyncLoaderがある場合、resolveを遅延し親の_pendingResolveとチェーン | 非同期コンポーネント |
| BR-40-07 | data-v-属性スキップ | _setAttrでdata-v-で始まるattributeは処理しない | attribute変更時 |
| BR-40-08 | REMOVAL値 | _setPropで空のREMOVALオブジェクトをsentinelとして使い、propの削除を表現 | attribute除去時 |

### 計算ロジック

属性反映ロジック: `value === true`→`setAttribute(key, '')`、`typeof val === 'string' || typeof val === 'number'`→`setAttribute(key, val+'')`、`!val`→`removeAttribute(key)`

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発時警告 | 既存のShadow DOMがあるのにSSR対応でない | `Custom element has pre-rendered declarative shadow root but is not defined as hydratable.` |
| - | 開発時警告 | shadowRoot=falseでスタイルが指定された | `Custom element style injection is not supported when using shadowRoot: false` |
| - | 開発時警告 | exposedプロパティ名がカスタム要素の既存プロパティと衝突 | `Exposed property "${key}" already exists on custom element.` |
| - | 開発時警告 | useHost()がコンポーネント外で呼ばれた | `useHost called without an active component instance.` |
| - | 開発時警告 | useHost()がdefineCustomElement以外のコンポーネントで呼ばれた | `useHost can only be used in components defined via defineCustomElement.` |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- `@__NO_SIDE_EFFECTS__`アノテーションによりtree-shaking対応
- `_styleChildren`のWeakSetにより重複スタイル注入を防止
- `_patching`フラグにより、Vueのpatch処理中の不要な外部更新トリガーを抑制
- `_dirty`フラグにより、patch中の変更をバッチ処理

## セキュリティ考慮事項

- `nonce`オプションによりCSP（Content Security Policy）対応のスタイル注入が可能
- Shadow DOMによるスタイルのカプセル化がセキュリティ上の利点を提供

## 備考

- `defineSSRCustomElement`: SSR対応のカスタム要素定義関数。内部的に`createSSRApp`を使用
- `useHost()`: カスタム要素のホスト要素（VueElement）にアクセスするフック
- `useShadowRoot()`: カスタム要素のShadow Rootにアクセスするフック
- `_isVueCE`: VueElementインスタンスの識別マーカー
- `VueElement._beginPatch`/`_endPatch`: patch処理中のフラグ管理
- `VueElement._injectChildStyle`: 子コンポーネントのスタイルをホスト要素に注入

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

CustomElementOptions、VueElementConstructor型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apiCustomElement.ts | `packages/runtime-dom/src/apiCustomElement.ts` | `CustomElementOptions`（53-59行目）、`VueElementConstructor`（49-51行目） |

#### Step 2: defineCustomElement関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apiCustomElement.ts | `packages/runtime-dom/src/apiCustomElement.ts` | `defineCustomElement`（168-186行目） |

**主要処理フロー**:
1. **176行目**: `defineComponent(options, extraOptions)`でコンポーネント正規化
2. **177行目**: プレーンオブジェクトならextendで拡張
3. **178-185行目**: `VueCustomElement`クラス定義（VueElementを継承）

#### Step 3: VueElementクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apiCustomElement.ts | `packages/runtime-dom/src/apiCustomElement.ts` | `VueElement`クラス（203-751行目） |

**主要処理フロー**:
1. **249-279行目**: コンストラクタ - Shadow DOM生成
2. **281-316行目**: `connectedCallback` - 接続時処理
3. **336-354行目**: `disconnectedCallback` - 切断時処理
4. **365-427行目**: `_resolveDef` - コンポーネント定義解決
5. **429-457行目**: `_mount` - Appインスタンス生成・マウント
6. **459-481行目**: `_resolveProps` - getter/setter定義
7. **504-541行目**: `_setProp` - プロパティ設定・属性反映
8. **549-600行目**: `_createVNode` - VNode生成・emit変換

#### Step 4: 関連フックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | apiCustomElement.ts | `packages/runtime-dom/src/apiCustomElement.ts` | `useHost`（753-771行目）、`useShadowRoot`（777-780行目） |

### プログラム呼び出し階層図

```
defineCustomElement(options, extraOptions)
    │
    ├─ defineComponent(options, extraOptions)
    │
    └─ class VueCustomElement extends VueElement
           │
           ├─ constructor(initialProps)
           │      └─ super(Comp, initialProps, createApp)
           │             └─ attachShadow({ mode: 'open' })
           │
           ├─ connectedCallback()
           │      ├─ 親VueElement検索
           │      ├─ _resolveDef()
           │      │      ├─ MutationObserver設定
           │      │      ├─ resolve()
           │      │      │      ├─ Number型props変換
           │      │      │      ├─ _resolveProps() → getter/setter
           │      │      │      ├─ _applyStyles()
           │      │      │      └─ _mount()
           │      │      └─ (async) __asyncLoader().then(resolve)
           │      └─ _mount()
           │             ├─ createApp(def)
           │             ├─ _inheritParentContext()
           │             ├─ configureApp(app)
           │             ├─ app.mount(_root)
           │             └─ exposed property定義
           │
           ├─ disconnectedCallback()
           │      └─ app.unmount() + cleanup
           │
           └─ _createVNode()
                  ├─ createVNode(def, props)
                  └─ vnode.ce = instance => {
                         ├─ instance.emit = dispatchEvent(CustomEvent)
                         └─ _setParent()
                     }
```

### データフロー図

```
[入力]                     [処理]                              [出力]

Vueコンポーネント定義 ┌──────────────────────────┐
  (Options/Setup) ───▶│ defineCustomElement       │
                    │  ├ defineComponent           │
                    │  └ VueCustomElementクラス生成 │──────▶ VueElementConstructor
                    └──────────────────────────┘

DOM挿入              ┌──────────────────────────┐
  (<my-element>) ───▶│ connectedCallback          │
                    │  ├ Shadow DOM生成           │──────▶ ShadowRoot
                    │  ├ createApp + mount        │──────▶ Vueインスタンス
                    │  ├ スタイル注入              │──────▶ <style>要素
                    │  └ emit → CustomEvent       │──────▶ DOMイベント
                    └──────────────────────────┘

attribute変更        ┌──────────────────────────┐
  (MutationObserver)─▶│ _setAttr → _setProp        │
                    │  └ _update → render          │──────▶ DOM更新
                    └──────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apiCustomElement.ts | `packages/runtime-dom/src/apiCustomElement.ts` | ソース | defineCustomElement/VueElementのメイン実装 |
| index.ts | `packages/runtime-dom/src/index.ts` | ソース | defineCustomElementのエクスポート |
| component.ts | `packages/runtime-core/src/component.ts` | ソース | ComponentCustomElementInterfaceの定義 |
| customElement.spec.ts | `packages/runtime-dom/__tests__/customElement.spec.ts` | テスト | defineCustomElementのテスト |
