# 事件响应式机制

## 事件派发机制
[EventDispatcher](https://vjmap.com/map3d/api/classes/EventDispatcher.html) 类是可调度事件的所有类的基类


### 常用方法

#### dispatch()

> **dispatch**(`event`, ...`data`): `boolean`

触发指定事件名称.

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | Event name |
| ...`data` | `any` | Data |

***

#### eventNames()

> **eventNames**(): `string`[]

返回注册的所有事件名.

***

#### hasEvent()

> **hasEvent**(`event`): `boolean`

确定是否存在事件侦听

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | 事件名 |

***

#### listenerCount()

> **listenerCount**(`event`): `number`

返回具有指定事件名称的侦听总数.

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | Event name |

***

#### off()

> **off**(`event`, `fn`?): [`EventDispatcher`](event.md)

移除监听.

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | Event name |
| `fn`? | `Function` | Function, If is undefined, delete all corresponding event listeners. |

***

#### on()

> **on**(`event`, `fn`): [`EventDispatcher`](event.md)

增加监听

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | Event name |
| `fn` | `Function` | Function |

***

#### once()

> **once**(`event`, `fn`): [`EventDispatcher`](event.md)

只监听一次.

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event` | `string` | Event name |
| `fn` | `Function` | Function |

***

#### removeAllEventListeners()

> **removeAllEventListeners**(`event`?): `void`

通过事件名移除所有监听.

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `event`? | `string` | Event name, delete all events if not passed |


## 信号机制 

[Signal](https://vjmap.com/map3d/api/classes/Signal.html) 信号是一种事件总线。您可以使用`add`方法订阅事件，并使用`dispatch`方法进行调度。

 信号与正常事件总线的不同之处在于，1个信号对应于1个事件类型。例如，在`EventDispatcher`中，您用 `on` 监听不同名称的事件，它允许您订阅任何类型的事件，比如把 `onMouseClick` 做为参数。使用`Signal`，您将获得一个对应于`onMouseClick`的信号，并仅为此事件调度此信号。

 示例如下（`onCameraUpdate` 等少数事件同时走两条通道）:

 `EventDispatcher`写法:

 ```
 app.on("onCameraUpdate", e => console.log("onCameraUpdate"));
 ```

 `Signal`写法:

 ```
 app.signal.onCameraUpdate.add(e => console.log("onCameraUpdate"));
 ```

⚠️ **`onXxx` 事件名 vs DOM 事件名**：`onCameraUpdate`/`onMarker2D`/`onMarker3D`/`onPopup2D` 等源码里 `app.dispatch("onXxx")` 与 `app.signal.onXxx.dispatch(...)` 各发一次，两种写法等价；但鼠标交互事件的派发类型名是 **DOM 事件名**——`InteractionManager.sendEvent` 里 `app.dispatch("click", e)` + `app.signal.onMouseClick.dispatch(e)` 同时发生。所以 `app.on("click")` 和 `app.signal.onMouseClick.add(...)` 都能收到点击；但 `app.on("onMouseClick")` 永远收不到（没有这个名字的事件被 dispatch）。


### 常用方法



#### add()

> **add**(`h`, `context`?): () => `boolean`

增加一个信号处理回调函数

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `h` | `any` |  |
| `context`? | `any` |  |


***

#### addOne()

> **addOne**(`h`, `context`?): () => `boolean`

增加一次信号处理回调函数，执行一次后会自动移除

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `h` | `any` |  |
| `context`? | `any` |  |

***


#### contains()

> **contains**(`h`): `boolean`

信号处理回调函数中是否包含这个函数

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `h` | `any` |  |


***

#### dispatch()

> **dispatch**(...`args`): `void`

派发事件，触发事件回调

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| ...`args` | `any`[] |  |


***


#### remove()

> **remove**(`h`, `thisArg`?): `boolean`

从监听的信号处理回调函数中移除

##### Parameters

| Parameter | Type | Description |
| :------ | :------ | :------ |
| `h` | `any` |  |
| `thisArg`? | `any` | if supplied, will match handlers with a specific context only |

***

#### removeAll()

> **removeAll**(): `void`

移除所有监听的信号处理回调函数


## 响应式机制

`vjmap3d`的响应式参考了`vue3`的响应式机制。具体可参考`vue3`的相关文档 [响应式基础](https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html) [深入响应式系统](https://cn.vuejs.org/guide/extras/reactivity-in-depth.html)

相关示例用法
```js
const { ref, reactive, shallowRef, shallowReactive, computed, watch, watchEffect, effectScope} = vjmap3d.reactivity;
const scope = effectScope()
let ob = reactive({d: 3})
watch(ob, (newValue, oldValue) => console.log("watch ob", newValue, oldValue), { immediate: true });
scope.run(() => {
    let objValue = {a: 1}
    let obj = ref(objValue);
    let num = ref(3);
    const c = computed(() => num.value + ob.d)
    watch(obj, (newValue, oldValue) => console.log("watch", newValue, oldValue), { immediate: true });
    const unwatchEffect = watchEffect((onCleanup) => console.log("watchEffect", obj.value));
    //
    const unwatch = watch([() => obj.value, ob], ([new1, new2], [old1, old2]) => {
        console.log("watch m", new1, new2, old1, old2)
    },{ immediate: true } )
    //
    obj.value = {a: 2}
    //
    let num2 = shallowRef(1);
    watch(num2, (n, o, onCleanup)=>{console.log(n,o); onCleanup(()=>console.log("clear"))}, { immediate: true })
    //
    let sobj = shallowReactive({a: 2});
    watch(sobj, (n, o, onCleanup)=>{console.log(n,o); onCleanup(()=>console.log("clear"))}, { immediate: true })
    //
    unwatchEffect();
    unwatch();
    obj.value = {b: 3}
    console.log(c.value)
    ob.d = 4
    console.log(c.value)
});
ob.d = 5
scope.stop()
ob.d = 7
ob.d = 8
```

在 `vjmap3d` 当响应式对象数据发生变化时，可以用`watch`或`watchEffect`进行监听其状态变化。

比如监听一个实体是否处于辉光，示例如下:


<mapFrame  showCode="true">
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    camera: {
        fov: 75,
        near: 0.1, 
        far: 1000,
        position: new THREE.Vector3(0, 0, 5)
    }
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的对象
entity.addTo(app);// 把实体加进应用中
// 间隔三秒设置
setInterval(() => {
    entity.bloom = !entity.bloom; // 设置实体辉光
}, 3000)
// 监听响应式对象变化
vjmap3d.reactivity.watch(entity.bloomRef, (v) => app.logInfo(v ? "设置了辉光" : "没有辉光", 1000))
</pre>
</mapFrame>
