]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): add app.onUnmount() for registering cleanup functions (#4619)
authorThorsten Lünborg <t.luenborg@googlemail.com>
Mon, 29 Apr 2024 10:47:56 +0000 (12:47 +0200)
committerGitHub <noreply@github.com>
Mon, 29 Apr 2024 10:47:56 +0000 (18:47 +0800)
close #4516

packages/runtime-core/__tests__/apiCreateApp.spec.ts
packages/runtime-core/src/apiCreateApp.ts
packages/runtime-core/src/errorHandling.ts

index f638633974132d3eef367f72d75d30c7e9ace141..0d7c338031143b3f062042fef50af2ee5d860a71 100644 (file)
@@ -344,6 +344,36 @@ describe('api: createApp', () => {
     ).toHaveBeenWarnedTimes(1)
   })
 
+  test('onUnmount', () => {
+    const cleanup = vi.fn().mockName('plugin cleanup')
+    const PluginA: Plugin = app => {
+      app.provide('foo', 1)
+      app.onUnmount(cleanup)
+    }
+    const PluginB: Plugin = {
+      install: (app, arg1, arg2) => {
+        app.provide('bar', arg1 + arg2)
+        app.onUnmount(cleanup)
+      },
+    }
+
+    const app = createApp({
+      render: () => `Test`,
+    })
+    app.use(PluginA)
+    app.use(PluginB)
+
+    const root = nodeOps.createElement('div')
+    app.mount(root)
+
+    //also can be added after mount
+    app.onUnmount(cleanup)
+
+    app.unmount()
+
+    expect(cleanup).toHaveBeenCalledTimes(3)
+  })
+
   test('config.errorHandler', () => {
     const error = new Error()
     const count = ref(0)
index 65c10166de7cbc8f600fca727c29aa86fbc36a0f..286eb2bcc8e801b96e4b37db0490478c1ed8bdc7 100644 (file)
@@ -27,6 +27,7 @@ import { version } from '.'
 import { installAppCompatProperties } from './compat/global'
 import type { NormalizedPropsOptions } from './componentProps'
 import type { ObjectEmitsOptions } from './componentEmits'
+import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
 import type { DefineComponent } from './apiDefineComponent'
 
 export interface App<HostElement = any> {
@@ -50,6 +51,7 @@ export interface App<HostElement = any> {
     namespace?: boolean | ElementNamespace,
   ): ComponentPublicInstance
   unmount(): void
+  onUnmount(cb: () => void): void
   provide<T>(key: InjectionKey<T> | string, value: T): this
 
   /**
@@ -214,6 +216,7 @@ export function createAppAPI<HostElement>(
 
     const context = createAppContext()
     const installedPlugins = new WeakSet()
+    const pluginCleanupFns: Array<() => any> = []
 
     let isMounted = false
 
@@ -366,8 +369,23 @@ export function createAppAPI<HostElement>(
         }
       },
 
+      onUnmount(cleanupFn: () => void) {
+        if (__DEV__ && typeof cleanupFn !== 'function') {
+          warn(
+            `Expected function as first argument to app.onUnmount(), ` +
+              `but got ${typeof cleanupFn}`,
+          )
+        }
+        pluginCleanupFns.push(cleanupFn)
+      },
+
       unmount() {
         if (isMounted) {
+          callWithAsyncErrorHandling(
+            pluginCleanupFns,
+            app._instance,
+            ErrorCodes.APP_UNMOUNT_CLEANUP,
+          )
           render(null, app._container)
           if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
             app._instance = null
index 41c92cbd34a967f46614f59a344b675118f0b620..beddea5f6442c8170d4ea0e25a2c7a192c9c069b 100644 (file)
@@ -23,6 +23,7 @@ export enum ErrorCodes {
   FUNCTION_REF,
   ASYNC_COMPONENT_LOADER,
   SCHEDULER,
+  APP_UNMOUNT_CLEANUP,
 }
 
 export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
@@ -57,6 +58,7 @@ export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
   [ErrorCodes.SCHEDULER]:
     'scheduler flush. This is likely a Vue internals bug. ' +
     'Please open an issue at https://github.com/vuejs/core .',
+  [ErrorCodes.APP_UNMOUNT_CLEANUP]: 'app unmount cleanup function',
 }
 
 export type ErrorTypes = LifecycleHooks | ErrorCodes