mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 01:03:03 +08:00
feat(vue): add composition API ionic lifecycle hooks (#22970)
resolves #22769
This commit is contained in:
@ -1,5 +1,12 @@
|
|||||||
import { BackButtonEvent } from '@ionic/core';
|
import { BackButtonEvent } from '@ionic/core';
|
||||||
import { inject, ref, Ref } from 'vue';
|
import {
|
||||||
|
inject,
|
||||||
|
ref,
|
||||||
|
Ref,
|
||||||
|
ComponentInternalInstance,
|
||||||
|
getCurrentInstance
|
||||||
|
} from 'vue';
|
||||||
|
import { LifecycleHooks } from './utils';
|
||||||
|
|
||||||
type Handler = (processNextHandler: () => void) => Promise<any> | void | null;
|
type Handler = (processNextHandler: () => void) => Promise<any> | void | null;
|
||||||
|
|
||||||
@ -62,3 +69,38 @@ export const useKeyboard = (): IonKeyboardRef => {
|
|||||||
unregister
|
unregister
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an returns a function that
|
||||||
|
* can be used to provide a lifecycle hook.
|
||||||
|
*/
|
||||||
|
const injectHook = (lifecycleType: LifecycleHooks, hook: Function, component: ComponentInternalInstance | null): Function | undefined => {
|
||||||
|
if (component) {
|
||||||
|
|
||||||
|
// Add to public instance so it is accessible to IonRouterOutlet
|
||||||
|
const target = component as any;
|
||||||
|
const hooks = target.proxy[lifecycleType] || (target.proxy[lifecycleType] = []);
|
||||||
|
const wrappedHook = (...args: unknown[]) => {
|
||||||
|
if (target.isUnmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return args ? hook(...args) : hook();
|
||||||
|
};
|
||||||
|
|
||||||
|
hooks.push(wrappedHook);
|
||||||
|
|
||||||
|
return wrappedHook;
|
||||||
|
} else {
|
||||||
|
console.warn('[@ionic/vue]: Ionic Lifecycle Hooks can only be used during execution of setup().');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createHook = <T extends Function = () => any>(lifecycle: LifecycleHooks) => {
|
||||||
|
return (hook: T, target: ComponentInternalInstance | null = getCurrentInstance()) => injectHook(lifecycle, hook, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onIonViewWillEnter = createHook(LifecycleHooks.WillEnter);
|
||||||
|
export const onIonViewDidEnter = createHook(LifecycleHooks.DidEnter);
|
||||||
|
export const onIonViewWillLeave = createHook(LifecycleHooks.WillLeave);
|
||||||
|
export const onIonViewDidLeave = createHook(LifecycleHooks.DidLeave);
|
||||||
|
@ -16,7 +16,17 @@ export { IonApp } from './components/IonApp';
|
|||||||
|
|
||||||
export * from './components/Overlays';
|
export * from './components/Overlays';
|
||||||
|
|
||||||
export { IonKeyboardRef, IonRouter, useBackButton, useIonRouter, useKeyboard } from './hooks';
|
export {
|
||||||
|
IonKeyboardRef,
|
||||||
|
IonRouter,
|
||||||
|
useBackButton,
|
||||||
|
useIonRouter,
|
||||||
|
useKeyboard,
|
||||||
|
onIonViewWillEnter,
|
||||||
|
onIonViewDidEnter,
|
||||||
|
onIonViewWillLeave,
|
||||||
|
onIonViewDidLeave
|
||||||
|
} from './hooks';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
modalController,
|
modalController,
|
||||||
|
@ -3,6 +3,19 @@ import { Config as CoreConfig, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYC
|
|||||||
|
|
||||||
type LIFECYCLE_EVENTS = typeof LIFECYCLE_WILL_ENTER | typeof LIFECYCLE_DID_ENTER | typeof LIFECYCLE_WILL_LEAVE | typeof LIFECYCLE_DID_LEAVE;
|
type LIFECYCLE_EVENTS = typeof LIFECYCLE_WILL_ENTER | typeof LIFECYCLE_DID_ENTER | typeof LIFECYCLE_WILL_LEAVE | typeof LIFECYCLE_DID_LEAVE;
|
||||||
|
|
||||||
|
export enum LifecycleHooks {
|
||||||
|
WillEnter = 'onIonViewWillEnter',
|
||||||
|
DidEnter = 'onIonViewDidEnter',
|
||||||
|
WillLeave = 'onIonViewWillLeave',
|
||||||
|
DidLeave = 'onIonViewDidLeave'
|
||||||
|
}
|
||||||
|
const hookNames = {
|
||||||
|
[LIFECYCLE_WILL_ENTER]: LifecycleHooks.WillEnter,
|
||||||
|
[LIFECYCLE_DID_ENTER]: LifecycleHooks.DidEnter,
|
||||||
|
[LIFECYCLE_WILL_LEAVE]: LifecycleHooks.WillLeave,
|
||||||
|
[LIFECYCLE_DID_LEAVE]: LifecycleHooks.DidLeave
|
||||||
|
}
|
||||||
|
|
||||||
const ids: { [k: string]: number } = { main: 0 };
|
const ids: { [k: string]: number } = { main: 0 };
|
||||||
|
|
||||||
export const generateId = (type = 'main') => {
|
export const generateId = (type = 'main') => {
|
||||||
@ -21,6 +34,18 @@ export const fireLifecycle = (vueComponent: any, vueInstance: Ref<ComponentPubli
|
|||||||
if (instance?.[lifecycle]) {
|
if (instance?.[lifecycle]) {
|
||||||
instance[lifecycle]();
|
instance[lifecycle]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire any Composition API
|
||||||
|
* Ionic Lifecycle hooks
|
||||||
|
*/
|
||||||
|
if (instance) {
|
||||||
|
const hook = hookNames[lifecycle];
|
||||||
|
const hooks = instance[hook];
|
||||||
|
if (hooks) {
|
||||||
|
hooks.forEach((hook: Function) => hook());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getConfig = (): CoreConfig | null => {
|
export const getConfig = (): CoreConfig | null => {
|
||||||
|
@ -22,6 +22,11 @@
|
|||||||
ionViewWillLeave: <div id="willLeave">{{ willLeave }}</div><br />
|
ionViewWillLeave: <div id="willLeave">{{ willLeave }}</div><br />
|
||||||
ionViewDidLeave: <div id="didLeave">{{ didLeave }}</div><br />
|
ionViewDidLeave: <div id="didLeave">{{ didLeave }}</div><br />
|
||||||
|
|
||||||
|
onIonViewWillEnter: <div id="onWillEnter">{{ onWillEnter }}</div><br />
|
||||||
|
onIonViewDidEnter: <div id="onDidEnter">{{ onDidEnter }}</div><br />
|
||||||
|
onIonViewWillLeave: <div id="onWillLeave">{{ onWillLeave }}</div><br />
|
||||||
|
onIonViewDidLeave: <div id="onDidLeave">{{ onDidLeave }}</div><br />
|
||||||
|
|
||||||
<ion-button router-link="/navigation" id="lifecycle-navigation">Go to another page</ion-button>
|
<ion-button router-link="/navigation" id="lifecycle-navigation">Go to another page</ion-button>
|
||||||
</div>
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
@ -37,9 +42,13 @@ import {
|
|||||||
IonHeader,
|
IonHeader,
|
||||||
IonPage,
|
IonPage,
|
||||||
IonTitle,
|
IonTitle,
|
||||||
IonToolbar
|
IonToolbar,
|
||||||
|
onIonViewWillEnter,
|
||||||
|
onIonViewDidEnter,
|
||||||
|
onIonViewWillLeave,
|
||||||
|
onIonViewDidLeave
|
||||||
} from '@ionic/vue';
|
} from '@ionic/vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
IonButton,
|
IonButton,
|
||||||
@ -65,6 +74,24 @@ export default defineComponent({
|
|||||||
this.didLeave++;
|
this.didLeave++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const onWillEnter = ref(0);
|
||||||
|
const onDidEnter = ref(0);
|
||||||
|
const onWillLeave = ref(0);
|
||||||
|
const onDidLeave = ref(0);
|
||||||
|
|
||||||
|
onIonViewWillEnter(() => onWillEnter.value += 1);
|
||||||
|
onIonViewDidEnter(() => onDidEnter.value += 1);
|
||||||
|
onIonViewWillLeave(() => onWillLeave.value += 1);
|
||||||
|
onIonViewDidLeave(() => onDidLeave.value += 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
onWillEnter,
|
||||||
|
onDidEnter,
|
||||||
|
onWillLeave,
|
||||||
|
onDidLeave
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
willEnter: 0,
|
willEnter: 0,
|
||||||
|
@ -7,7 +7,11 @@ describe('Lifecycle', () => {
|
|||||||
ionViewWillEnter: 1,
|
ionViewWillEnter: 1,
|
||||||
ionViewDidEnter: 1,
|
ionViewDidEnter: 1,
|
||||||
ionViewWillLeave: 0,
|
ionViewWillLeave: 0,
|
||||||
ionViewDidLeave: 0
|
ionViewDidLeave: 0,
|
||||||
|
onIonViewWillEnter: 1,
|
||||||
|
onIonViewDidEnter: 1,
|
||||||
|
onIonViewWillLeave: 0,
|
||||||
|
onIonViewDidLeave: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('#lifecycle-navigation').click();
|
cy.get('#lifecycle-navigation').click();
|
||||||
@ -16,7 +20,11 @@ describe('Lifecycle', () => {
|
|||||||
ionViewWillEnter: 1,
|
ionViewWillEnter: 1,
|
||||||
ionViewDidEnter: 1,
|
ionViewDidEnter: 1,
|
||||||
ionViewWillLeave: 1,
|
ionViewWillLeave: 1,
|
||||||
ionViewDidLeave: 1
|
ionViewDidLeave: 1,
|
||||||
|
onIonViewWillEnter: 1,
|
||||||
|
onIonViewDidEnter: 1,
|
||||||
|
onIonViewWillLeave: 1,
|
||||||
|
onIonViewDidLeave: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.ionBackClick('navigation');
|
cy.ionBackClick('navigation');
|
||||||
@ -25,7 +33,11 @@ describe('Lifecycle', () => {
|
|||||||
ionViewWillEnter: 2,
|
ionViewWillEnter: 2,
|
||||||
ionViewDidEnter: 2,
|
ionViewDidEnter: 2,
|
||||||
ionViewWillLeave: 1,
|
ionViewWillLeave: 1,
|
||||||
ionViewDidLeave: 1
|
ionViewDidLeave: 1,
|
||||||
|
onIonViewWillEnter: 2,
|
||||||
|
onIonViewDidEnter: 2,
|
||||||
|
onIonViewWillLeave: 1,
|
||||||
|
onIonViewDidLeave: 1
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -36,7 +48,11 @@ describe('Lifecycle', () => {
|
|||||||
ionViewWillEnter: 1,
|
ionViewWillEnter: 1,
|
||||||
ionViewDidEnter: 1,
|
ionViewDidEnter: 1,
|
||||||
ionViewWillLeave: 0,
|
ionViewWillLeave: 0,
|
||||||
ionViewDidLeave: 0
|
ionViewDidLeave: 0,
|
||||||
|
onIonViewWillEnter: 1,
|
||||||
|
onIonViewDidEnter: 1,
|
||||||
|
onIonViewWillLeave: 0,
|
||||||
|
onIonViewDidLeave: 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -46,4 +62,9 @@ const testLifecycle = (selector, expected = {}) => {
|
|||||||
cy.get(`[data-pageid=${selector}] #didEnter`).should('have.text', expected.ionViewDidEnter);
|
cy.get(`[data-pageid=${selector}] #didEnter`).should('have.text', expected.ionViewDidEnter);
|
||||||
cy.get(`[data-pageid=${selector}] #willLeave`).should('have.text', expected.ionViewWillLeave);
|
cy.get(`[data-pageid=${selector}] #willLeave`).should('have.text', expected.ionViewWillLeave);
|
||||||
cy.get(`[data-pageid=${selector}] #didLeave`).should('have.text', expected.ionViewDidLeave);
|
cy.get(`[data-pageid=${selector}] #didLeave`).should('have.text', expected.ionViewDidLeave);
|
||||||
|
|
||||||
|
cy.get(`[data-pageid=${selector}] #onWillEnter`).should('have.text', expected.onIonViewWillEnter);
|
||||||
|
cy.get(`[data-pageid=${selector}] #onDidEnter`).should('have.text', expected.onIonViewDidEnter);
|
||||||
|
cy.get(`[data-pageid=${selector}] #onWillLeave`).should('have.text', expected.onIonViewWillLeave);
|
||||||
|
cy.get(`[data-pageid=${selector}] #onDidLeave`).should('have.text', expected.onIonViewDidLeave);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"module": "es2015",
|
"module": "es2015",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": false,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"outDir": "dist-transpiled",
|
"outDir": "dist-transpiled",
|
||||||
|
Reference in New Issue
Block a user