> **本文回答**：setDOMContent + Vue / React 组件 —— 动态更新、事件绑定、组件卸载。

## 一、setDOMContent 的用法

setHTML 用字符串，setDOMContent 用现成的 Node：

```js
const div = document.createElement("div");
div.innerHTML = `
  <h3>站点</h3>
  <button id="btn-save">保存</button>
`;
div.querySelector("#btn-save").addEventListener("click", () => alert("save"));

new vjmap.Popup().setDOMContent(div).setLngLat([lng, lat]).addTo(map);
```

优点：事件直接绑定，popup 打开后立即生效。

## 二、Vue 3 组件

### 2.1 渲染函数

```vue
<!-- SiteCard.vue -->
<template>
  <div class="card">
    <h3>{{ site.name }}</h3>
    <p>温度 {{ site.temp }}℃</p>
    <button @click="$emit('save')">保存</button>
  </div>
</template>
```

```js
import { createApp, h } from "vue";
import SiteCard from "./SiteCard.vue";

function makeCardPopup(site, handlers) {
  const container = document.createElement("div");
  const app = createApp({
    render: () => h(SiteCard, { site, onSave: handlers.onSave })
  });
  app.mount(container);
  const popup = new vjmap.Popup().setDOMContent(container);
  popup.once("close", () => app.unmount());  // 关闭时销毁；用 once：close 会重复触发（见 §八）
  return popup;
}

const popup = makeCardPopup(siteA, { onSave: () => console.log("saved") });
popup.setLngLat([lng, lat]).addTo(map);
```

### 2.2 Teleport 到 map 容器

或者用 `<Teleport>`：让 Popup DOM 结构保留在组件树内，但视觉上挂到 popup 容器。这要求你能拿到 popup.getElement()。

## 三、React 18

```jsx
import ReactDOM from "react-dom/client";
import SiteCard from "./SiteCard";

function makeCardPopup(site) {
  const container = document.createElement("div");
  const root = ReactDOM.createRoot(container);
  root.render(<SiteCard site={site} />);
  const popup = new vjmap.Popup().setDOMContent(container);
  popup.once("close", () => root.unmount());   // once：close 会重复触发（见 §八）
  return popup;
}
```

## 四、更新 Popup 内容（不关闭）

```js
popup.setHTML("新内容");       // 整体替换
popup.setText("纯文本");
popup.setDOMContent(newDiv);    // 新的 DOM 节点
```

或直接操作 DOM：

```js
const contentEl = popup.getElement().querySelector(".vjmapgis-popup-content");
contentEl.querySelector(".temperature").textContent = "28℃";
```

## 五、异步加载数据

```js
const loader = document.createElement("div");
loader.textContent = "loading...";
const popup = new vjmap.Popup().setDOMContent(loader).setLngLat(ll).addTo(map);

fetchSite(id).then(data => {
  const card = document.createElement("div");
  card.innerHTML = `<h3>${data.name}</h3>`;
  popup.setDOMContent(card);
});
```

## 六、Vue 响应式 + Popup

在 Vue 3 setup 里：

```js
import { ref, watch } from "vue";
const temp = ref(26);

const card = document.createElement("div");
const app = createApp({
  setup() {
    return { temp };
  },
  template: `<div>温度 {{ temp }}℃</div>`
});
app.mount(card);

const popup = new vjmap.Popup().setDOMContent(card).setLngLat(ll).addTo(map);

setInterval(() => temp.value = Math.random() * 40, 1000);
```

## 七、表单 / input 在 Popup

```html
<div id="form">
  <input type="text" placeholder="名称" />
  <button>确定</button>
</div>
```

```js
const el = document.getElementById("form").cloneNode(true);
const popup = new vjmap.Popup({ focusAfterOpen: false }).setDOMContent(el);
popup.setLngLat(ll).addTo(map);

el.querySelector("button").addEventListener("click", () => {
  alert("name: " + el.querySelector("input").value);
});
```

**注意**：设 `focusAfterOpen: false` 避免和输入法干扰。

## 八、Popup 关闭时清理

⚠️ `close` 在**每次 `remove()`** 时都触发（含从未打开时调 remove、对已打开的 popup 再 `addTo` 时）；且引擎 remove 时漏解绑 `preclick`，`closeOnClick: true` 的 popup 关闭后**每次点地图还会再触发 close**。清理逻辑必须幂等：

```js
let disposed = false;
popup.on("close", () => {
  if (disposed) return;
  disposed = true;
  reactRoot.unmount();              // React
  vueApp.unmount();                 // Vue
  clearInterval(timer);             // 定时器
  controller.abort();               // fetch controller
});
```

要复用 popup 再次打开时，在打开处把 `disposed` 复位并重新挂载内容。

## 九、性能：复用 Popup

不要每次都 `new vjmap.Popup()`。保持一个单例：

```js
const popup = new vjmap.Popup({ closeButton: false });

function showPopup(lngLat, content) {
  popup.setDOMContent(content).setLngLat(lngLat);
  if (!popup.isOpen()) popup.addTo(map);
}
```

## 十、常见坑

1. **Vue/React 没 unmount → 内存泄漏**：close 事件里清理（须幂等，见 §八）。
2. **setHTML 里的 `<script>` 不执行**（内部用 innerHTML）；内联 `onclick="fn()"` 属性**会**执行，但只能调全局函数且受 CSP 限制——推荐 setDOMContent + addEventListener。
3. **动态组件 props 更新** → 子组件能响应式更新；但 Popup 位置需另外 setLngLat。
4. **Popup 里放 map 组件** → 不支持嵌套地图；规避。
