getsentry / getsentry/sentry-javascript
[Nuxt 5] Replace the Vue integration’s reliance on `app.mixin()`
- Vorherrschende Sprache
- TypeScript
- Sterne
- 8.7k
- Forks
- 1.8k
- Ø Merge
- 1 T. 17 Std.
- Gemergte PRs (30 T.)
- 515
Beschreibung
Nuxt 5 disables Vue’s Options API by default
Refernce: [https://github.com/nuxt/nuxt/pull/35791]()
For the time being, it can be enabled in Nuxt with:
```ts
export default defineNuxtConfig({
vue: {
optionsApi: true
}
})
```
---
### What `app.mixin()` captures today
`vueInit()` in `packages/vue/src/integration.ts` registers one mixin when tracing is on. That mixin produces every Vue UI span we emit:
| Span | Op | Operation | Mixin hooks (start / end) | On by default |
|---|---|---|---|---|
| `Application Render` | `ui.render` | any | the root's first hook starts it, a `timeout` debounce (2000 ms) ends it | yes |
| `Vue ` | `ui.mount` | `mount` | `beforeMount` / `mounted` | yes |
| `Vue ` | `ui.mount` | `mount` | `beforeMount` / `mounted` | no (`trackComponents`) |
| `Vue ` | `ui.mount` | `activate` | `activated` / `deactivated` | no (`trackComponents`) |
| `Vue ` | `ui.mount` | `create` | `beforeCreate` / `created` | no (`trackComponents` + `hooks: ['create']`) |
| `Vue ` | `ui.update` | `update` | `beforeUpdate` / `updated` | no (`trackComponents` + `hooks: ['update']`) |
| `Vue ` | `ui.unmount` | `unmount` (Vue 3) | `beforeUnmount` / `unmounted` | no (`trackComponents` + `hooks: ['unmount']`) |
| `Vue ` | `ui.unmount` | `destroy` (Vue 2) | `beforeDestroy` / `destroyed` | no (`trackComponents` + `hooks: ['destroy']`) |
**Not affected**: Pageload and navigation transactions come from `browserTracingIntegration` and the router. Error capture uses `app.config.errorHandler`, which Vue does not gate. Pinia and TanStack Router never touch mixins.
### The alternative: register Composition API hooks in `setup`
Composition API lifecycle hooks are not gated by `__VUE_OPTIONS_API__`. They write to `instance.m`, `instance.u`, and so on, and the renderer invokes those arrays unconditionally. The question is only how to reach every component's `setup`.
Two mechanisms, and the split runs between the root component and everything else.
**Runtime, for the root.** `createApp` stores the root component as `app._component`, a plain writable property, and shallow-copies it first, so mutating `app._component.setup` in `vueInit()` does not touch the user's module. Function root components are not copied, so replace the property instead of mutating it. This covers both default-on spans with no plugin and no new install step.
**Build time, for `trackComponents`.** A Vite, Rollup, or webpack plugin adds a `setup` to each `.vue` module's default export that registers the hooks and then calls the original `setup`. Wrap by adding to the existing options object rather than returning a new component, so `name` and `__file` survive.
| Operation | Composition API hook | Runtime root wrap | Build-time plugin |
|---|---|---|---|
| `mount` (default on) | `onBeforeMount` / `onMounted` | `Application Render` and `Vue `, identical to the mixin | every `.vue` the plugin transforms |
| `activate` (default on) | `onActivated` / `onDeactivated` | never fires, the root is not inside `` | every `.vue` the plugin transforms |
| `update` (opt-in) | `onBeforeUpdate` / `onUpdated` | root re-renders only | every `.vue` the plugin transforms |
| `unmount` (opt-in) | `onBeforeUnmount` / `onUnmounted` | `app.unmount()` only | every `.vue` the plugin transforms |
| `create` (opt-in) | none, `setup` is the create phase | approximate, brackets the original `setup` | approximate, same |
Component names stay correct. `compiler-sfc` emits `__name` in production builds, and `formatComponentName` already reads it.
What it misses: render function and (non SFC) JSX components, runtime compiled templates, CDN builds, and pre-compiled components in `node_modules`, including Nuxt's own `` and ``, unless the plugin also transforms `node_modules`.
Nuxt can get the plugin through the existing `addVitePlugin` call in `packages/nuxt/src/module.ts`. Plain `@sentry/vue` users need a new install step.
### Vue 2 keeps the mixin
We cannot wrap `setup` at runtime in Vue 2, and Vue 2.7 does not change that.
The runtime wrap needs a handle on the root component's options object before Vue instantiates it. In Vue 3, `createApp(App)` gives us exactly that as `app._component`. In Vue 2 the integration receives the `Vue` constructor, the user calls `new Vue({...})` afterwards, and that options object never reaches us. Vue 2.7 backported the Composition API but explicitly did not port `createApp`, and 2.7 is the last Vue 2 minor.
### Warning users on missing mixins
`app.mixin()` is a silent no-op when `__VUE_OPTIONS_API__` is `false`, and Vue only warns in dev.
We can detect if mixins are disabled and we warn the users: https://github.com/getsentry/sentry-javascript/pull/23568
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.