vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.6k stars 8.32k forks source link

Properties of objects that inherit from EventTarget are not reactive #9037

Open LeaVerou opened 1 year ago

LeaVerou commented 1 year ago

Vue version

3.3.4

Link to minimal reproduction

https://codepen.io/leaverou/pen/WNLQQJa?editors=1110

Steps to reproduce

  1. Visit testcase
  2. Press "Increase foo.x". Notice how the value updates.
  3. Press "Increase bar.x". The value does not update

Also note in the console that app.foo is wrapped by a Proxy whereas app.bar is not.

What is expected?

bar should behave identically to foo

What is actually happening?

bar.x is not reactive.

System Info

No response

Any additional comments?

I wonder if this is intentional behavior to avoid wrapping DOM nodes and other built-ins. In that case, checking for its children explicitly might be a better choice, since extending EventTarget is the Web Platform's currently recommended way for authors to create data objects that can emit and listen to events. Or, perhaps there is a clever test to distinguish built-in EventTarget descendants from author objects. Anyhow, it would be unfortunate if supporting events in one's objects breaks Vue — especially since this seems like a small improvement I can easily see people doing thinking it cannot break anything (e.g. see articles like this)

leopiccionia commented 1 year ago

Vue only allow ordinary objects and a few built-ins (Array, Map, etc.) to be made reactive (source).

The crux is how Vue currently checks if an object x is ordinary: it checks if x.toString() equals "[object Object]" (source).

While foo.toString() returns "[object Object]", bar.toString() returns "[object EventTarget]". I've checked that overriding the Symbol.toString getter in the class to return "Object", although hacky-ish, indeed fix the reactivity problem (at least on the provided demo).

LeaVerou commented 1 year ago

Thanks, that's a great workaround! Hacky, yes, and fragile, but could work for now. Leaving this open in hopes for a less hacky solution down the line.