From: Eduardo San Martin Morote Date: Wed, 22 May 2019 06:46:17 +0000 (+0200) Subject: wip: listening to history X-Git-Tag: v4.0.0-alpha.0~366 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=09be1747cfb006bfaa1562374b9672918ebd3a68;p=thirdparty%2Fvuejs%2Frouter.git wip: listening to history --- diff --git a/.gitignore b/.gitignore index 9ded42c1..198792ca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ examples_dist node_modules coverage .nyc_output +.rpt2_cache diff --git a/explorations/html5.html b/explorations/html5.html index e3e0b259..bec4eec7 100644 --- a/explorations/html5.html +++ b/explorations/html5.html @@ -1,11 +1,17 @@ - - - - - Testing History HTML5 - - - + + + + + Testing History HTML5 + + +
+ +
+ diff --git a/explorations/html5.ts b/explorations/html5.ts index 34f25759..ac52cc59 100644 --- a/explorations/html5.ts +++ b/explorations/html5.ts @@ -1,6 +1,13 @@ import { Router, HTML5History } from '../src' import { RouteComponent } from '../src/types' +declare global { + interface Window { + cancel: boolean + } +} +window.cancel = false + const component: RouteComponent = { template: `
A component
`, } @@ -20,6 +27,7 @@ const r = new Router({ routes: [ { path: '/', component }, { path: '/users/:id', name: 'user', component }, + { path: '/n/:n', name: 'increment', component }, { path: '/multiple/:a/:b', name: 'user', component }, { path: '/with-guard/:n', @@ -50,6 +58,11 @@ r.beforeEach((to, from, next) => { next() }) +r.beforeEach((to, from, next) => { + if (window.cancel) return next(false) + next() +}) + r.afterEach((to, from) => { console.log( `After guard: from ${from.fullPath} to ${ @@ -74,6 +87,7 @@ window.r = r h.listen((to, from, { type }) => { console.log(`popstate(${type})`, { to, from }) }) + async function run() { // r.push('/multiple/one/two') diff --git a/src/history/base.ts b/src/history/base.ts index b61cd720..1c0580ca 100644 --- a/src/history/base.ts +++ b/src/history/base.ts @@ -57,6 +57,7 @@ export abstract class BaseHistory { // previousState: object location: HistoryLocationNormalized = START base: string = '' + paused: boolean = false utils = utils /** @@ -73,6 +74,13 @@ export abstract class BaseHistory { */ abstract replace(to: HistoryLocation): void + /** + * Goes back in history log. Should trigger any listener added via + * `listen`. If we are on the first entry, behaviour may change depending + * on implementation + */ + abstract back(): void + /** * Notifies back whenever the location changes due to user interactions * outside of the applicaiton. For example, going back/forward on a diff --git a/src/history/html5.ts b/src/history/html5.ts index adb3da4c..1a111c4a 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -67,6 +67,8 @@ export class HTML5History extends BaseHistory { push(to: HistoryLocation, data?: HistoryState) { // replace current entry state to add the forward value + // TODO: should be removed and let the user normalize the location? + // or make it fast so normalization on a normalized object is fast const normalized = this.utils.normalizeLocation(to) this.history.replaceState( buildState( @@ -89,6 +91,11 @@ export class HTML5History extends BaseHistory { this.location = normalized } + back() { + // TODO: do not trigger listen + this.history.back() + } + listen(callback: NavigationCallback) { // settup the listener and prepare teardown callbacks this._listeners.push(callback) @@ -122,6 +129,10 @@ export class HTML5History extends BaseHistory { state, location: this.location, }) + if (this.paused) { + cs.info('Ignored beacuse paused') + return + } const from = this.location // we have the state from the old entry, not the current one being removed // TODO: correctly parse pathname diff --git a/src/history/utils.ts b/src/history/utils.ts index 902ece63..a0aa5892 100644 --- a/src/history/utils.ts +++ b/src/history/utils.ts @@ -52,7 +52,6 @@ export function parseURL(location: string): HistoryLocationNormalized { * @param search */ export function parseQuery(search: string): HistoryQuery { - // TODO: optimize by using a for loop const hasLeadingIM = search[0] === '?' const query: HistoryQuery = {} const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&') diff --git a/src/matcher.ts b/src/matcher.ts index f5fa85a7..f5cdf4be 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -13,6 +13,8 @@ interface RouteMatcher { resolve: (params?: RouteParams) => string record: RouteRecord // TODO: NormalizedRouteRecord? parent: RouteMatcher | void + // TODO: children so they can be removed + // children: RouteMatcher[] keys: string[] } diff --git a/src/router.ts b/src/router.ts index 6cb0eee3..b5a99937 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,8 @@ -import { BaseHistory, HistoryLocationNormalized } from './history/base' +import { + BaseHistory, + HistoryLocationNormalized, + NavigationType, +} from './history/base' import { RouterMatcher } from './matcher' import { RouteLocation, @@ -35,14 +39,39 @@ export class Router { this.matcher = new RouterMatcher(options.routes) - this.history.listen((to, from, info) => { + this.history.listen(async (to, from, info) => { const matchedRoute = this.matchLocation(to, this.currentRoute) // console.log({ to, matchedRoute }) // TODO: navigate & guards + const toLocation: RouteLocationNormalized = { ...to, ...matchedRoute } + + try { + await this.navigate(toLocation, this.currentRoute) - this.currentRoute = { - ...to, - ...matchedRoute, + // accept current navigation + this.currentRoute = { + ...to, + ...matchedRoute, + } + } catch (error) { + // TODO: use the push/replace techieque with any navigation to + // preserve history when moving forward + if (error instanceof NavigationGuardRedirect) { + this.push(error.to) + } else { + // TODO: handle abort and redirect correctly + // if we were going back, we push and discard the rest of the history + if (info.type === NavigationType.back) { + this.history.push(from) + } else { + // TODO: go back because we cancelled, then + // or replace and not discard the rest of history. Check issues, there was one talking about this + // behaviour, maybe we can do better + this.history.paused = true + this.history.back() + this.history.paused = false + } + } } }) } @@ -84,6 +113,13 @@ export class Router { } } + async push(to: RouteLocation): Promise { + // match the location + const { url, location } = + let url: HistoryLocationNormalized + let location: MatcherLocationNormalized + } + /** * Trigger a navigation, adding an entry to the history stack. Also apply all navigation * guards first @@ -92,6 +128,7 @@ export class Router { async push(to: RouteLocation): Promise { let url: HistoryLocationNormalized let location: MatcherLocationNormalized + // TODO: refactor into matchLocation to return location and url if (typeof to === 'string' || 'path' in to) { url = this.history.utils.normalizeLocation(to) // TODO: should allow a non matching url to allow dynamic routing to work @@ -232,7 +269,8 @@ export class Router { beforeEach(guard: NavigationGuard): ListenerRemover { this.beforeGuards.push(guard) return () => { - this.beforeGuards.splice(this.beforeGuards.indexOf(guard), 1) + const i = this.beforeGuards.indexOf(guard) + if (i > -1) this.beforeGuards.splice(i, 1) } } @@ -243,7 +281,8 @@ export class Router { afterEach(guard: PostNavigationGuard): ListenerRemover { this.afterGuards.push(guard) return () => { - this.afterGuards.splice(this.afterGuards.indexOf(guard), 1) + const i = this.afterGuards.indexOf(guard) + if (i > -1) this.afterGuards.splice(i, 1) } } }