mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
chore(router): clean up code and add comments (#23355)
This commit is contained in:
@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
|||||||
|
|
||||||
import { AnimationBuilder, BackButtonEvent, RouteChain, RouterDirection, RouterEventDetail } from '../../interface';
|
import { AnimationBuilder, BackButtonEvent, RouteChain, RouterDirection, RouterEventDetail } from '../../interface';
|
||||||
import { debounce } from '../../utils/helpers';
|
import { debounce } from '../../utils/helpers';
|
||||||
|
import { NavigationHookResult } from '../route/route-interface';
|
||||||
|
|
||||||
import { ROUTER_INTENT_BACK, ROUTER_INTENT_FORWARD, ROUTER_INTENT_NONE } from './utils/constants';
|
import { ROUTER_INTENT_BACK, ROUTER_INTENT_FORWARD, ROUTER_INTENT_NONE } from './utils/constants';
|
||||||
import { printRedirects, printRoutes } from './utils/debug';
|
import { printRedirects, printRoutes } from './utils/debug';
|
||||||
@ -186,6 +187,7 @@ export class Router implements ComponentInterface {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This handler gets called when a `ion-route-redirect` component is added to the DOM or if the from or to property of such node changes.
|
||||||
private onRedirectChanged() {
|
private onRedirectChanged() {
|
||||||
const path = this.getPath();
|
const path = this.getPath();
|
||||||
if (path && findRouteRedirect(path, readRedirects(this.el))) {
|
if (path && findRouteRedirect(path, readRedirects(this.el))) {
|
||||||
@ -193,6 +195,7 @@ export class Router implements ComponentInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This handler gets called when a `ion-route` component is added to the DOM or if the from or to property of such node changes.
|
||||||
private onRoutesChanged() {
|
private onRoutesChanged() {
|
||||||
return this.writeNavStateRoot(this.getPath(), ROUTER_INTENT_NONE);
|
return this.writeNavStateRoot(this.getPath(), ROUTER_INTENT_NONE);
|
||||||
}
|
}
|
||||||
@ -202,7 +205,7 @@ export class Router implements ComponentInterface {
|
|||||||
|
|
||||||
if (win.history.state === null) {
|
if (win.history.state === null) {
|
||||||
this.state++;
|
this.state++;
|
||||||
win.history.replaceState(this.state, win.document.title, win.document.location && win.document.location.href);
|
win.history.replaceState(this.state, win.document.title, win.document.location?.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = win.history.state;
|
const state = win.history.state;
|
||||||
@ -211,11 +214,11 @@ export class Router implements ComponentInterface {
|
|||||||
|
|
||||||
if (state > lastState || (state >= lastState && lastState > 0)) {
|
if (state > lastState || (state >= lastState && lastState > 0)) {
|
||||||
return ROUTER_INTENT_FORWARD;
|
return ROUTER_INTENT_FORWARD;
|
||||||
} else if (state < lastState) {
|
}
|
||||||
|
if (state < lastState) {
|
||||||
return ROUTER_INTENT_BACK;
|
return ROUTER_INTENT_BACK;
|
||||||
} else {
|
|
||||||
return ROUTER_INTENT_NONE;
|
|
||||||
}
|
}
|
||||||
|
return ROUTER_INTENT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async writeNavStateRoot(path: string[] | null, direction: RouterDirection, animation?: AnimationBuilder): Promise<boolean> {
|
private async writeNavStateRoot(path: string[] | null, direction: RouterDirection, animation?: AnimationBuilder): Promise<boolean> {
|
||||||
@ -281,7 +284,7 @@ export class Router implements ComponentInterface {
|
|||||||
//
|
//
|
||||||
// When the beforeLeave hook does not return true (to allow navigating) then that value is returned early and the beforeEnter is executed.
|
// When the beforeLeave hook does not return true (to allow navigating) then that value is returned early and the beforeEnter is executed.
|
||||||
// Otherwise the beforeEnterHook hook of the target route is executed.
|
// Otherwise the beforeEnterHook hook of the target route is executed.
|
||||||
private async runGuards(to: string[] | null = this.getPath(), from?: string[] | null) {
|
private async runGuards(to: string[] | null = this.getPath(), from?: string[] | null): Promise<NavigationHookResult> {
|
||||||
if (from === undefined) {
|
if (from === undefined) {
|
||||||
from = parsePath(this.previousPath).segments;
|
from = parsePath(this.previousPath).segments;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,5 +88,5 @@ const searchNavNode = (root: HTMLElement | undefined): NavOutletElement | undefi
|
|||||||
return root as NavOutletElement;
|
return root as NavOutletElement;
|
||||||
}
|
}
|
||||||
const outlet = root.querySelector<NavOutletElement>(QUERY);
|
const outlet = root.querySelector<NavOutletElement>(QUERY);
|
||||||
return outlet ? outlet : undefined;
|
return outlet ?? undefined;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
import { RouteChain, RouteNode, RouteRedirect, RouteTree } from './interface';
|
import { RouteChain, RouteNode, RouteRedirect, RouteTree } from './interface';
|
||||||
import { parsePath } from './path';
|
import { parsePath } from './path';
|
||||||
|
|
||||||
|
const readProp = (el: HTMLElement, prop: string): string | null | undefined => {
|
||||||
|
if (prop in el) {
|
||||||
|
return (el as any)[prop];
|
||||||
|
}
|
||||||
|
if (el.hasAttribute(prop)) {
|
||||||
|
return el.getAttribute(prop);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export const readRedirects = (root: Element): RouteRedirect[] => {
|
export const readRedirects = (root: Element): RouteRedirect[] => {
|
||||||
return (Array.from(root.children) as HTMLIonRouteRedirectElement[])
|
return (Array.from(root.children) as HTMLIonRouteRedirectElement[])
|
||||||
.filter(el => el.tagName === 'ION-ROUTE-REDIRECT')
|
.filter(el => el.tagName === 'ION-ROUTE-REDIRECT')
|
||||||
@ -17,46 +27,33 @@ export const readRoutes = (root: Element): RouteChain[] => {
|
|||||||
return flattenRouterTree(readRouteNodes(root));
|
return flattenRouterTree(readRouteNodes(root));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const readRouteNodes = (root: Element, node = root): RouteTree => {
|
export const readRouteNodes = (node: Element): RouteTree => {
|
||||||
return (Array.from(node.children) as HTMLIonRouteElement[])
|
return (Array.from(node.children) as HTMLIonRouteElement[])
|
||||||
.filter(el => el.tagName === 'ION-ROUTE' && el.component)
|
.filter(el => el.tagName === 'ION-ROUTE' && el.component)
|
||||||
.map(el => {
|
.map(el => {
|
||||||
const component = readProp(el, 'component');
|
const component = readProp(el, 'component') as string;
|
||||||
if (component == null) {
|
|
||||||
throw new Error('component missing in ion-route');
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
path: parsePath(readProp(el, 'url')).segments,
|
path: parsePath(readProp(el, 'url')).segments,
|
||||||
id: component.toLowerCase(),
|
id: component.toLowerCase(),
|
||||||
params: el.componentProps,
|
params: el.componentProps,
|
||||||
beforeLeave: el.beforeLeave,
|
beforeLeave: el.beforeLeave,
|
||||||
beforeEnter: el.beforeEnter,
|
beforeEnter: el.beforeEnter,
|
||||||
children: readRouteNodes(root, el)
|
children: readRouteNodes(el)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const readProp = (el: HTMLElement, prop: string): string | null | undefined => {
|
|
||||||
if (prop in el) {
|
|
||||||
return (el as any)[prop];
|
|
||||||
}
|
|
||||||
if (el.hasAttribute(prop)) {
|
|
||||||
return el.getAttribute(prop);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const flattenRouterTree = (nodes: RouteTree): RouteChain[] => {
|
export const flattenRouterTree = (nodes: RouteTree): RouteChain[] => {
|
||||||
const routes: RouteChain[] = [];
|
const chains: RouteChain[] = [];
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
flattenNode([], routes, node);
|
flattenNode([], chains, node);
|
||||||
}
|
}
|
||||||
return routes;
|
return chains;
|
||||||
};
|
};
|
||||||
|
|
||||||
const flattenNode = (chain: RouteChain, routes: RouteChain[], node: RouteNode) => {
|
const flattenNode = (chain: RouteChain, chains: RouteChain[], node: RouteNode) => {
|
||||||
const s = chain.slice();
|
chain = chain.slice();
|
||||||
s.push({
|
chain.push({
|
||||||
id: node.id,
|
id: node.id,
|
||||||
path: node.path,
|
path: node.path,
|
||||||
params: node.params,
|
params: node.params,
|
||||||
@ -65,10 +62,10 @@ const flattenNode = (chain: RouteChain, routes: RouteChain[], node: RouteNode) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (node.children.length === 0) {
|
if (node.children.length === 0) {
|
||||||
routes.push(s);
|
chains.push(chain);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const sub of node.children) {
|
for (const child of node.children) {
|
||||||
flattenNode(s, routes, sub);
|
flattenNode(chain, chains, child);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ROUTER_INTENT_FORWARD } from './constants';
|
import { ROUTER_INTENT_FORWARD } from './constants';
|
||||||
import { ParsedRoute, RouteChain, RouterDirection } from './interface';
|
import { ParsedRoute, RouteChain, RouterDirection } from './interface';
|
||||||
|
|
||||||
|
// Join the non empty segments with "/".
|
||||||
export const generatePath = (segments: string[]): string => {
|
export const generatePath = (segments: string[]): string => {
|
||||||
const path = segments
|
const path = segments
|
||||||
.filter(s => s.length > 0)
|
.filter(s => s.length > 0)
|
||||||
@ -9,6 +10,26 @@ export const generatePath = (segments: string[]): string => {
|
|||||||
return '/' + path;
|
return '/' + path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateUrl = (segments: string[], useHash: boolean, queryString?: string) => {
|
||||||
|
let url = generatePath(segments);
|
||||||
|
if (useHash) {
|
||||||
|
url = '#' + url;
|
||||||
|
}
|
||||||
|
if (queryString !== undefined) {
|
||||||
|
url += '?' + queryString;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const writePath = (history: History, root: string, useHash: boolean, path: string[], direction: RouterDirection, state: number, queryString?: string) => {
|
||||||
|
const url = generateUrl([...parsePath(root).segments, ...path], useHash, queryString);
|
||||||
|
if (direction === ROUTER_INTENT_FORWARD) {
|
||||||
|
history.pushState(state, '', url);
|
||||||
|
} else {
|
||||||
|
history.replaceState(state, '', url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const chainToPath = (chain: RouteChain): string[] | null => {
|
export const chainToPath = (chain: RouteChain): string[] | null => {
|
||||||
const path = [];
|
const path = [];
|
||||||
for (const route of chain) {
|
for (const route of chain) {
|
||||||
@ -27,25 +48,12 @@ export const chainToPath = (chain: RouteChain): string[] | null => {
|
|||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const writePath = (history: History, root: string, useHash: boolean, path: string[], direction: RouterDirection, state: number, queryString?: string) => {
|
// Remove the prefix segments from the path segments.
|
||||||
let url = generatePath([
|
//
|
||||||
...parsePath(root).segments,
|
// Return:
|
||||||
...path
|
// - null when the path segments do not start with the passed prefix,
|
||||||
]);
|
// - the path segments after the prefix otherwise.
|
||||||
if (useHash) {
|
const removePrefix = (prefix: string[], path: string[]): string[] | null => {
|
||||||
url = '#' + url;
|
|
||||||
}
|
|
||||||
if (queryString !== undefined) {
|
|
||||||
url = url + '?' + queryString;
|
|
||||||
}
|
|
||||||
if (direction === ROUTER_INTENT_FORWARD) {
|
|
||||||
history.pushState(state, '', url);
|
|
||||||
} else {
|
|
||||||
history.replaceState(state, '', url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removePrefix = (prefix: string[], path: string[]): string[] | null => {
|
|
||||||
if (prefix.length > path.length) {
|
if (prefix.length > path.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -53,7 +61,7 @@ export const removePrefix = (prefix: string[], path: string[]): string[] | null
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < prefix.length; i++) {
|
for (let i = 0; i < prefix.length; i++) {
|
||||||
if (prefix[i].length > 0 && prefix[i] !== path[i]) {
|
if (prefix[i] !== path[i]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,15 +72,8 @@ export const removePrefix = (prefix: string[], path: string[]): string[] | null
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readPath = (loc: Location, root: string, useHash: boolean): string[] | null => {
|
export const readPath = (loc: Location, root: string, useHash: boolean): string[] | null => {
|
||||||
let pathname = loc.pathname;
|
|
||||||
if (useHash) {
|
|
||||||
const hash = loc.hash;
|
|
||||||
pathname = (hash[0] === '#')
|
|
||||||
? hash.slice(1)
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefix = parsePath(root).segments;
|
const prefix = parsePath(root).segments;
|
||||||
|
const pathname = useHash ? loc.hash.slice(1) : loc.pathname;
|
||||||
const path = parsePath(pathname).segments;
|
const path = parsePath(pathname).segments;
|
||||||
return removePrefix(prefix, path);
|
return removePrefix(prefix, path);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user