From 47a85aab743749107d9b6773d2d7bfe135df0908 Mon Sep 17 00:00:00 2001
From: nilibing <nilibing@xindong.com>
Date: Tue, 26 Oct 2021 16:18:13 +0800
Subject: [PATCH] feat: ability to check push vs replace in navigation guard

issue #1620
pull request #1906
---
 examples/index.html                 |  1 +
 examples/push-or-replace/app.js     | 77 +++++++++++++++++++++++++++++
 examples/push-or-replace/index.html |  6 +++
 src/history/hash.js                 |  6 +++
 src/history/html5.js                |  6 +++
 src/util/location.js                |  3 +-
 src/util/route.js                   |  1 +
 7 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 examples/push-or-replace/app.js
 create mode 100644 examples/push-or-replace/index.html

diff --git a/examples/index.html b/examples/index.html
index 5f2cd4f32..535035ad6 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -27,6 +27,7 @@ <h1>Vue Router Examples</h1>
     <li><a href="auth-flow">Auth Flow</a></li>
     <li><a href="discrete-components">Discrete Components</a></li>
     <li><a href="nested-router">Nested Routers</a></li>
+    <li><a href="push-or-replace">Push Or Replace</a></li>
     <li><a href="keepalive-view">Keepalive View</a></li>
     <li><a href="multi-app">Multiple Apps</a></li>
     <li><a href="restart-app">Restart App</a></li>
diff --git a/examples/push-or-replace/app.js b/examples/push-or-replace/app.js
new file mode 100644
index 000000000..82f96d3c0
--- /dev/null
+++ b/examples/push-or-replace/app.js
@@ -0,0 +1,77 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+
+Vue.use(VueRouter)
+
+const store = Vue.observable({
+  replace: false,
+  time: new Date().getTime()
+})
+
+const computed = {
+  replace () {
+    return store.replace
+  },
+  time () {
+    return store.time
+  }
+}
+
+const Home = { computed, template: '<div>home {{replace}} {{time}}</div>' }
+const Page = { computed, template: '<div>page {{replace}} {{time}}</div>' }
+const Detail = { computed, template: '<div>detail {{replace}} {{time}}</div>' }
+
+const router = new VueRouter({
+  mode: 'history',
+  base: __dirname,
+  routes: [
+    { path: '/', component: Home },
+
+    { path: '/page', component: Page },
+
+    { path: '/detail', component: Detail }
+
+  ]
+})
+
+// User can check the replace type in navigation guard, and do anything they want.
+router.beforeEach((to, from, next) => {
+  store.replace = to.replace
+  store.time = new Date().getTime()
+  next()
+})
+
+new Vue({
+  router,
+  template: `
+    <div id="app">
+      <h1>Push Or Replace</h1>
+      <p>User can check the replace type in navigation guard, and do anything they want.</p>
+      <pre>
+router.beforeEach((to, from, next) => {
+  if (to.replace) {
+    to.query.replace = true
+  }
+  else {
+    to.query.replace = false
+  }
+  if (to && to.query && !to.query.time) {
+    to.query.time = new Date().getTime()
+    next(to)
+  } else {
+    next()
+  }
+})
+      </pre>
+      <ul>
+        <li><router-link to="/">/</router-link></li>
+        <li><router-link to="/page">/page</router-link> ( push )</li>
+        <li><a @click="$router.push('/page')">/page</a> $router.push('/page') </li>
+        <li><router-link to="/detail" replace>/detail</router-link> ( replace )</li>
+        <li><a @click="$router.replace('/detail')">/detail</a> $router.replace('/detail') </li>
+        <li><a @click="$router.go(-1)">back</a> $router.go(-1) </li>
+      </ul>
+      <router-view class="view"></router-view>
+    </div>
+  `
+}).$mount('#app')
diff --git a/examples/push-or-replace/index.html b/examples/push-or-replace/index.html
new file mode 100644
index 000000000..0b88284f9
--- /dev/null
+++ b/examples/push-or-replace/index.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="stylesheet" href="/global.css">
+<a href="/">&larr; Examples index</a>
+<div id="app"></div>
+<script src="/__build__/shared.chunk.js"></script>
+<script src="/__build__/push-or-replace.js"></script>
diff --git a/src/history/hash.js b/src/history/hash.js
index e0ddf2d7c..4b5bccf6d 100644
--- a/src/history/hash.js
+++ b/src/history/hash.js
@@ -71,6 +71,12 @@ export class HashHistory extends History {
 
   replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
     const { current: fromRoute } = this
+    if (typeof location === 'string') {
+      location = { path: location }
+    }
+    if (typeof location === 'object') {
+      (location: Object).replace = true
+    }
     this.transitionTo(
       location,
       route => {
diff --git a/src/history/html5.js b/src/history/html5.js
index faaa02fcd..058d70846 100644
--- a/src/history/html5.js
+++ b/src/history/html5.js
@@ -66,6 +66,12 @@ export class HTML5History extends History {
 
   replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
     const { current: fromRoute } = this
+    if (typeof location === 'string') {
+      location = { path: location }
+    }
+    if (typeof location === 'object') {
+      (location: Object).replace = true
+    }
     this.transitionTo(location, route => {
       replaceState(cleanPath(this.base + route.fullPath))
       handleScroll(this.router, route, fromRoute, false)
diff --git a/src/util/location.js b/src/util/location.js
index fb98d4537..a2f72f817 100644
--- a/src/util/location.js
+++ b/src/util/location.js
@@ -64,6 +64,7 @@ export function normalizeLocation (
     _normalized: true,
     path,
     query,
-    hash
+    hash,
+    replace: !!next.replace
   }
 }
diff --git a/src/util/route.js b/src/util/route.js
index 9d8e31cd1..9fdda7a7f 100644
--- a/src/util/route.js
+++ b/src/util/route.js
@@ -23,6 +23,7 @@ export function createRoute (
     meta: (record && record.meta) || {},
     path: location.path || '/',
     hash: location.hash || '',
+    replace: !!location.replace,
     query,
     params: location.params || {},
     fullPath: getFullPath(location, stringifyQuery),