mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +08:00
@ -110,7 +110,7 @@ describe('View Stacks', () => {
|
|||||||
const itemC = createRegisteredViewItem(viewStacks, 1, '/home/3', true);
|
const itemC = createRegisteredViewItem(viewStacks, 1, '/home/3', true);
|
||||||
const itemD = createRegisteredViewItem(viewStacks, 1, '/home/4', true);
|
const itemD = createRegisteredViewItem(viewStacks, 1, '/home/4', true);
|
||||||
|
|
||||||
viewStacks.unmountLeavingViews(1, itemA, itemD);
|
viewStacks.unmountLeavingViews(1, itemA, -3);
|
||||||
|
|
||||||
expect(itemB.mount).toEqual(false);
|
expect(itemB.mount).toEqual(false);
|
||||||
expect(itemB.ionPageElement).toEqual(undefined);
|
expect(itemB.ionPageElement).toEqual(undefined);
|
||||||
@ -127,7 +127,7 @@ describe('View Stacks', () => {
|
|||||||
const itemC = createRegisteredViewItem(viewStacks);
|
const itemC = createRegisteredViewItem(viewStacks);
|
||||||
const itemD = createRegisteredViewItem(viewStacks);
|
const itemD = createRegisteredViewItem(viewStacks);
|
||||||
|
|
||||||
viewStacks.mountIntermediaryViews(1, itemD, itemA);
|
viewStacks.mountIntermediaryViews(1, itemA, 3);
|
||||||
|
|
||||||
expect(itemB.mount).toEqual(true);
|
expect(itemB.mount).toEqual(true);
|
||||||
expect(itemC.mount).toEqual(true);
|
expect(itemC.mount).toEqual(true);
|
||||||
|
@ -262,6 +262,7 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
routeInfo.position = currentHistoryPosition;
|
routeInfo.position = currentHistoryPosition;
|
||||||
|
routeInfo.delta = delta;
|
||||||
const historySize = locationHistory.size();
|
const historySize = locationHistory.size();
|
||||||
const historyDiff = currentHistoryPosition - initialHistoryPosition;
|
const historyDiff = currentHistoryPosition - initialHistoryPosition;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ export interface RouteInfo {
|
|||||||
pushedByRoute?: string;
|
pushedByRoute?: string;
|
||||||
tab?: string;
|
tab?: string;
|
||||||
position?: number;
|
position?: number;
|
||||||
|
delta?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RouteParams {
|
export interface RouteParams {
|
||||||
|
@ -164,34 +164,6 @@ export const createViewStacks = (router: Router) => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a view stack and entering/leaving views,
|
|
||||||
* determine the position of each item in the stack.
|
|
||||||
* This is useful for removing/adding views in between
|
|
||||||
* the view items when navigating using router.go.
|
|
||||||
* Use this method instead of doing an `Array.findIndex`
|
|
||||||
* for both view items.
|
|
||||||
*/
|
|
||||||
const findViewIndex = (viewStack: ViewItem[], enteringViewItem: ViewItem, leavingViewItem: ViewItem) => {
|
|
||||||
let enteringIndex = -1;
|
|
||||||
let leavingIndex = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i <= viewStack.length - 1; i++) {
|
|
||||||
const viewItem = viewStack[i];
|
|
||||||
if (viewItem === enteringViewItem) {
|
|
||||||
enteringIndex = i;
|
|
||||||
} else if (viewItem === leavingViewItem) {
|
|
||||||
leavingIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enteringIndex > -1 && leavingIndex > -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { enteringIndex, leavingIndex };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When navigating backwards, we need to clean up and
|
* When navigating backwards, we need to clean up and
|
||||||
* leaving pages so that they are re-created if
|
* leaving pages so that they are re-created if
|
||||||
@ -199,13 +171,13 @@ export const createViewStacks = (router: Router) => {
|
|||||||
* important when using router.go and stepping back
|
* important when using router.go and stepping back
|
||||||
* multiple pages at a time.
|
* multiple pages at a time.
|
||||||
*/
|
*/
|
||||||
const unmountLeavingViews = (outletId: number, enteringViewItem: ViewItem, leavingViewItem: ViewItem) => {
|
const unmountLeavingViews = (outletId: number, viewItem: ViewItem, delta: number = 1) => {
|
||||||
const viewStack = viewStacks[outletId];
|
const viewStack = viewStacks[outletId];
|
||||||
if (!viewStack) return;
|
if (!viewStack) return;
|
||||||
|
|
||||||
const { enteringIndex: startIndex, leavingIndex: endIndex } = findViewIndex(viewStack, enteringViewItem, leavingViewItem);
|
const startIndex = viewStack.findIndex(v => v === viewItem);
|
||||||
|
|
||||||
for (let i = startIndex + 1; i < endIndex; i++) {
|
for (let i = startIndex + 1; i < startIndex - delta; i++) {
|
||||||
const viewItem = viewStack[i];
|
const viewItem = viewStack[i];
|
||||||
viewItem.mount = false;
|
viewItem.mount = false;
|
||||||
viewItem.ionPageElement = undefined;
|
viewItem.ionPageElement = undefined;
|
||||||
@ -219,14 +191,26 @@ export const createViewStacks = (router: Router) => {
|
|||||||
* developers to step forward over multiple views.
|
* developers to step forward over multiple views.
|
||||||
* The intermediary views need to be remounted so that
|
* The intermediary views need to be remounted so that
|
||||||
* swipe to go back works properly.
|
* swipe to go back works properly.
|
||||||
|
* We need to account for the delta value here too because
|
||||||
|
* we do not want to remount an unrelated view.
|
||||||
|
* Example:
|
||||||
|
* /home --> /page2 --> router.back() --> /page3
|
||||||
|
* Going to /page3 would remount /page2 since we do
|
||||||
|
* not prune /page2 from the stack. However, /page2
|
||||||
|
* needs to remain in the stack.
|
||||||
|
* Example:
|
||||||
|
* /home --> /page2 --> /page3 --> router.go(-2) --> router.go(2)
|
||||||
|
* We would end up on /page3, but users need to be able to swipe
|
||||||
|
* to go back to /page2 and /home, so we need both pages mounted
|
||||||
|
* in the DOM.
|
||||||
*/
|
*/
|
||||||
const mountIntermediaryViews = (outletId: number, enteringViewItem: ViewItem, leavingViewItem: ViewItem) => {
|
const mountIntermediaryViews = (outletId: number, viewItem: ViewItem, delta: number = 1) => {
|
||||||
const viewStack = viewStacks[outletId];
|
const viewStack = viewStacks[outletId];
|
||||||
if (!viewStack) return;
|
if (!viewStack) return;
|
||||||
|
|
||||||
const { enteringIndex: endIndex, leavingIndex: startIndex } = findViewIndex(viewStack, enteringViewItem, leavingViewItem);
|
const startIndex = viewStack.findIndex(v => v === viewItem);
|
||||||
|
|
||||||
for (let i = startIndex + 1; i < endIndex; i++) {
|
for (let i = startIndex + 1; i < startIndex + delta; i++) {
|
||||||
viewStack[i].mount = true;
|
viewStack[i].mount = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ export const IonRouterOutlet = defineComponent({
|
|||||||
|
|
||||||
const handlePageTransition = async () => {
|
const handlePageTransition = async () => {
|
||||||
const routeInfo = ionRouter.getCurrentRouteInfo();
|
const routeInfo = ionRouter.getCurrentRouteInfo();
|
||||||
const { routerDirection, routerAction, routerAnimation, prevRouteLastPathname } = routeInfo;
|
const { routerDirection, routerAction, routerAnimation, prevRouteLastPathname, delta } = routeInfo;
|
||||||
|
|
||||||
const enteringViewItem = viewStacks.findViewItemByRouteInfo(routeInfo, id, usingDeprecatedRouteSetup);
|
const enteringViewItem = viewStacks.findViewItemByRouteInfo(routeInfo, id, usingDeprecatedRouteSetup);
|
||||||
let leavingViewItem = viewStacks.findLeavingViewItemByRouteInfo(routeInfo, id, true, usingDeprecatedRouteSetup);
|
let leavingViewItem = viewStacks.findLeavingViewItemByRouteInfo(routeInfo, id, true, usingDeprecatedRouteSetup);
|
||||||
@ -286,10 +286,10 @@ See https://ionicframework.com/docs/vue/navigation#ionpage for more information.
|
|||||||
leavingViewItem.mount = false;
|
leavingViewItem.mount = false;
|
||||||
leavingViewItem.ionPageElement = undefined;
|
leavingViewItem.ionPageElement = undefined;
|
||||||
leavingViewItem.ionRoute = false;
|
leavingViewItem.ionRoute = false;
|
||||||
viewStacks.unmountLeavingViews(id, enteringViewItem, leavingViewItem);
|
viewStacks.unmountLeavingViews(id, enteringViewItem, delta);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewStacks.mountIntermediaryViews(id, enteringViewItem, leavingViewItem);
|
viewStacks.mountIntermediaryViews(id, leavingViewItem, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
fireLifecycle(leavingViewItem.vueComponent, leavingViewItem.vueComponentRef, LIFECYCLE_DID_LEAVE);
|
fireLifecycle(leavingViewItem.vueComponent, leavingViewItem.vueComponentRef, LIFECYCLE_DID_LEAVE);
|
||||||
|
@ -441,4 +441,104 @@ describe('Routing', () => {
|
|||||||
|
|
||||||
expect(beforeRouteEnterSpy).toHaveBeenCalledTimes(2);
|
expect(beforeRouteEnterSpy).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not mount intermediary components when delta is 1', async () => {
|
||||||
|
const Page = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
const Page2 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
const Page3 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{ path: '/page', component: Page },
|
||||||
|
{ path: '/page2', component: Page2 },
|
||||||
|
{ path: '/page3', component: Page3 },
|
||||||
|
{ path: '/', redirect: '/page' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
await router.isReady();
|
||||||
|
const wrapper = mount(App, {
|
||||||
|
global: {
|
||||||
|
plugins: [router, IonicVue]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page).exists()).toBe(true);
|
||||||
|
|
||||||
|
router.push('/page2');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page2).exists()).toBe(true);
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page2).exists()).toBe(false);
|
||||||
|
|
||||||
|
router.push('/page3');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page2).exists()).toBe(false);
|
||||||
|
expect(wrapper.findComponent(Page3).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unmount intermediary components when using router.go', async () => {
|
||||||
|
const Page = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
const Page2 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
const Page3 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page></ion-page>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{ path: '/page', component: Page },
|
||||||
|
{ path: '/page2', component: Page2 },
|
||||||
|
{ path: '/page3', component: Page3 },
|
||||||
|
{ path: '/', redirect: '/page' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
await router.isReady();
|
||||||
|
const wrapper = mount(App, {
|
||||||
|
global: {
|
||||||
|
plugins: [router, IonicVue]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/page2');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
router.push('/page3');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page2).exists()).toBe(true);
|
||||||
|
expect(wrapper.findComponent(Page3).exists()).toBe(true);
|
||||||
|
|
||||||
|
router.go(-2);
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Page).exists()).toBe(true);
|
||||||
|
expect(wrapper.findComponent(Page2).exists()).toBe(false);
|
||||||
|
expect(wrapper.findComponent(Page3).exists()).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user