From b0242cf9f5ff769852482bab925f53252fcc559f Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 3 Feb 2020 18:25:12 +0100 Subject: [PATCH] remove query normalization from history implementation --- playground/main.ts | 2 + src/history/common.ts | 90 +++++++++---------------------------------- src/history/html5.ts | 12 +----- src/index.ts | 3 -- src/router.ts | 26 ++++++------- src/types/index.ts | 4 +- 6 files changed, 36 insertions(+), 101 deletions(-) diff --git a/playground/main.ts b/playground/main.ts index 97771cb1..bfdfa541 100644 --- a/playground/main.ts +++ b/playground/main.ts @@ -1,3 +1,5 @@ +// necessary for webpack +/// import { createApp } from 'vue' import { router, routerHistory } from './router' import { globalState } from './store' diff --git a/src/history/common.ts b/src/history/common.ts index 7b2d31eb..1a79771b 100644 --- a/src/history/common.ts +++ b/src/history/common.ts @@ -1,19 +1,14 @@ import { ListenerRemover } from '../types' -import { encodeQueryProperty, encodeHash } from '../utils/encoding' +// import { encodeQueryProperty, encodeHash } from '../utils/encoding' -export type HistoryQuery = Record -// TODO: is it reall worth allowing null to form queries like ?q&b&c -// When parsing using URLSearchParams, `q&c=` yield an empty string for q and c -// I think it's okay to allow this by default and allow extending it -// a more permissive history query // TODO: allow numbers -export type RawHistoryQuery = Record +export type HistoryQuery = Record interface HistoryLocation { // pathname section path: string // search string parsed - query?: RawHistoryQuery + query?: HistoryQuery // hash with the # hash?: string } @@ -119,7 +114,7 @@ export function parseURL(location: string): HistoryLocationNormalized { ) // TODO: can we remove the normalize call? - query = normalizeQuery(parseQuery(searchString)) + query = parseQuery(searchString) } if (hashPos > -1) { @@ -139,34 +134,16 @@ export function parseURL(location: string): HistoryLocationNormalized { } } -// TODO: the encoding would be handled at a router level instead where encoding functions can be customized -// that way the matcher can encode/decode params properly - -// function safeDecodeUriComponent(value: string): string { -// try { -// value = decodeURIComponent(value) -// } catch (err) { -// // TODO: handling only URIError? -// console.warn( -// `[vue-router] error decoding query "${value}". Keeping the original value.` -// ) -// } - -// return value -// } - -// function safeEncodeUriComponent(value: string): string { -// try { -// value = encodeURIComponent(value) -// } catch (err) { -// // TODO: handling only URIError? -// console.warn( -// `[vue-router] error encoding query "${value}". Keeping the original value.` -// ) -// } +/** + * Stringify a URL object + * @param location + */ +export function stringifyURL(location: HistoryLocation): string { + let url = location.path + let query = location.query ? stringifyQuery(location.query) : '' -// return value -// } + return url + (query && '?' + query) + (location.hash || '') +} /** * Transform a queryString into a query object. Accept both, a version with the leading `?` and without @@ -196,23 +173,11 @@ export function parseQuery(search: string): HistoryQuery { } return query } - -/** - * Stringify a URL object - * @param location - */ -export function stringifyURL(location: HistoryLocation): string { - let url = location.path - let query = location.query ? stringifyQuery(location.query) : '' - - return url + (query && '?' + query) + encodeHash(location.hash || '') -} - /** * Stringify an object query. Works like URLSearchParams. Doesn't prepend a `?` * @param query */ -export function stringifyQuery(query: RawHistoryQuery): string { +export function stringifyQuery(query: HistoryQuery): string { let search = '' for (const key in query) { if (search.length > 1) search += '&' @@ -222,36 +187,19 @@ export function stringifyQuery(query: RawHistoryQuery): string { search += key continue } - const encodedKey = encodeQueryProperty(key) + // const encodedKey = encodeQueryProperty(key) let values: string[] = Array.isArray(value) ? value : [value] - const encodedValues = values.map(encodeQueryProperty) + // const encodedValues = values.map(encodeQueryProperty) - search += `${encodedKey}=${values[0]}` + search += `${key}=${values[0]}` for (let i = 1; i < values.length; i++) { - search += `&${encodedKey}=${encodedValues[i]}` + search += `&${key}=${values[i]}` } } return search } -export function normalizeQuery(query: RawHistoryQuery): HistoryQuery { - // TODO: properly test - const normalizedQuery: HistoryQuery = {} - for (const key in query) { - const value = query[key] - if (value === null) normalizedQuery[key] = '' - else normalizedQuery[key] = value - } - return normalizedQuery -} - -// use regular decodeURI -// Use a renamed export instead of global.decodeURI -// to support node and browser at the same time -const originalDecodeURI = decodeURI -export { originalDecodeURI as decodeURI } - /** * Normalize a History location object or string into a HistoryLocationNoramlized * @param location @@ -264,7 +212,7 @@ export function normalizeLocation( return { fullPath: stringifyURL(location), path: location.path, - query: location.query ? normalizeQuery(location.query) : {}, + query: location.query || {}, hash: location.hash || '', } } diff --git a/src/history/html5.ts b/src/history/html5.ts index 51c0e034..486a190a 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -1,14 +1,12 @@ import { RouterHistory, NavigationCallback, - parseQuery, normalizeLocation, stripBase, NavigationType, NavigationDirection, HistoryLocationNormalized, HistoryState, - parseURL, RawHistoryLocation, ValueContainer, } from './common' @@ -30,7 +28,6 @@ interface StateEntry { /** * Creates a noramlized history location from a window.location object - * TODO: encoding is not handled like this * @param location */ function createCurrentLocation( @@ -41,15 +38,10 @@ function createCurrentLocation( // allows hash based url if (base.indexOf('#') > -1) { // prepend the starting slash to hash so the url starts with /# - return parseURL(stripBase('/' + hash, base)) + return normalizeLocation(stripBase('/' + hash, base)) } const path = stripBase(pathname, base) - return { - fullPath: path + search + hash, - path, - query: parseQuery(search), - hash: hash, - } + return normalizeLocation(path + search + hash) } function useHistoryListeners( diff --git a/src/index.ts b/src/index.ts index 4f623a7f..6fadc302 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,9 +9,6 @@ import { } from './types' import { onBeforeRouteLeave } from './navigationGuards' -// necessary for webpack -/// - declare module 'vue' { function inject(key: InjectionKey | string): T | undefined function inject(key: InjectionKey | string, defaultValue: T): T diff --git a/src/router.ts b/src/router.ts index 089d0bc4..b598643d 100644 --- a/src/router.ts +++ b/src/router.ts @@ -12,12 +12,7 @@ import { TODO, Immutable, } from './types' -import { - RouterHistory, - normalizeLocation, - stringifyURL, - normalizeQuery, -} from './history/common' +import { RouterHistory, normalizeLocation } from './history/common' import { ScrollToPosition, ScrollPosition, @@ -31,8 +26,7 @@ import { } from './errors' import { extractComponentsGuards, guardToPromiseFn } from './utils' import { useCallbacks } from './utils/callbacks' -import { encodeParam } from './utils/encoding' -import { decode } from './utils/encoding' +import { encodeParam, decode } from './utils/encoding' import { ref, Ref, markNonReactive, nextTick, App } from 'vue' import { RouteRecordMatched } from './matcher/types' import Link from './components/Link' @@ -169,13 +163,11 @@ export function createRouter({ // target location normalized, used if we want to redirect again const normalizedLocation: RouteLocationNormalized = { ...matchedRoute.normalizedLocation, - fullPath: stringifyURL({ + ...normalizeLocation({ path: matchedRoute.normalizedLocation.path, query: location.query, hash: location.hash, }), - query: normalizeQuery(location.query || {}), - hash: location.hash, redirectedFrom, meta: {}, } @@ -205,7 +197,7 @@ export function createRouter({ return resolveLocation( { ...newLocation, - query: normalizeQuery(newLocation.query || {}), + query: newLocation.query || {}, hash: newLocation.hash || '', }, currentLocation, @@ -215,7 +207,7 @@ export function createRouter({ return resolveLocation( { ...redirect, - query: normalizeQuery(redirect.query || {}), + query: redirect.query || {}, hash: redirect.hash || '', }, currentLocation, @@ -384,9 +376,13 @@ export function createRouter({ record.leaveGuards = [] } - // change URL only if the user did a push/replace + // only consider as push if it's not the first navigation + const isFirstNavigation = from === START_LOCATION_NORMALIZED + + // change URL only if the user did a push/replace and if it's not the initial navigation because + // it's just reflecting the url if (isPush) { - if (replace) history.replace(toLocation) + if (replace || isFirstNavigation) history.replace(toLocation) else history.push(toLocation) } diff --git a/src/types/index.ts b/src/types/index.ts index 964f5755..08cfced8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import { HistoryQuery, RawHistoryQuery } from '../history/common' +import { HistoryQuery } from '../history/common' import { PathParserOptions } from '../matcher/path-parser-ranker' import { markNonReactive } from 'vue' import { RouteRecordMatched } from '../matcher/types' @@ -20,7 +20,7 @@ export type ListenerRemover = () => void export type RouteParams = Record export interface RouteQueryAndHash { - query?: RawHistoryQuery + query?: HistoryQuery hash?: string } export interface LocationAsPath { -- 2.47.3