diff --git a/examples/index.html b/examples/index.html index f2bdf7225..90a1018c8 100644 --- a/examples/index.html +++ b/examples/index.html @@ -25,6 +25,7 @@

Vue Router Examples

  • Auth Flow
  • Discrete Components
  • Nested Routers
  • +
  • Public reload method
  • diff --git a/examples/reload/app.js b/examples/reload/app.js new file mode 100644 index 000000000..ae1e5f590 --- /dev/null +++ b/examples/reload/app.js @@ -0,0 +1,142 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' +import Post from '../data-fetching/Post.vue' + +Vue.use(VueRouter) + +const Home = { template: '
    home
    ' } +const Foo = { template: '
    foo
    ' } +const Bar = { template: '
    bar
    ' } + +function guardRoute (to, from, next) { + if (window.confirm(`Navigate to ${to.path}?`)) { + next() + } else if (window.confirm(`Redirect to /baz?`)) { + next('/baz') + } else { + next(false) + } +} + +// Baz implements an in-component beforeRouteLeave hook +const Baz = { + data () { + return { saved: false } + }, + template: ` +
    +

    baz ({{ saved ? 'saved' : 'not saved' }})

    + +
    + `, + beforeRouteLeave (to, from, next) { + if (this.saved || window.confirm('Not saved, are you sure you want to navigate away?')) { + next() + } else { + next(false) + } + } +} + +// Baz implements an in-component beforeRouteEnter hook +const Qux = { + data () { + return { + msg: null + } + }, + template: `
    {{ msg }}
    `, + beforeRouteEnter (to, from, next) { + // Note that enter hooks do not have access to `this` + // because it is called before the component is even created. + // However, we can provide a callback to `next` which will + // receive the vm instance when the route has been confirmed. + // + // simulate an async data fetch. + // this pattern is useful when you want to stay at current route + // and only switch after the data has been fetched. + setTimeout(() => { + next(vm => { + vm.msg = 'Qux' + }) + }, 300) + } +} + +// Quux implements an in-component beforeRouteUpdate hook. +// this hook is called when the component is reused, but the route is updated. +// For example, when navigating from /quux/1 to /quux/2. +const Quux = { + data () { + return { + prevId: 0 + } + }, + template: `
    id:{{ $route.params.id }} prevId:{{ prevId }}
    `, + beforeRouteUpdate (to, from, next) { + this.prevId = from.params.id + next() + } +} + +const router = new VueRouter({ + mode: 'history', + base: __dirname, + routes: [ + { path: '/', component: Home }, + + // post with transitions + { path: '/post/:id', component: Post }, + + // inline guard + { path: '/foo', component: Foo, beforeEnter: guardRoute }, + + // using meta properties on the route config + // and check them in a global before hook + { path: '/bar', component: Bar, meta: { needGuard: true }}, + + // Baz implements an in-component beforeRouteLeave hook + { path: '/baz', component: Baz }, + + // Qux implements an in-component beforeRouteEnter hook + { path: '/qux', component: Qux }, + + // in-component beforeRouteEnter hook for async components + { path: '/qux-async', component: resolve => { + setTimeout(() => { + resolve(Qux) + }, 0) + } }, + + // in-component beforeRouteUpdate hook + { path: '/quux/:id', component: Quux } + ] +}) + +router.beforeEach((to, from, next) => { + if (to.matched.some(m => m.meta.needGuard)) { + guardRoute(to, from, next) + } else { + next() + } +}) + +new Vue({ + router, + template: ` +
    +

    reload() behaviors

    + + + +
    + ` +}).$mount('#app') diff --git a/examples/reload/index.html b/examples/reload/index.html new file mode 100644 index 000000000..cd4327611 --- /dev/null +++ b/examples/reload/index.html @@ -0,0 +1,6 @@ + + +← Examples index +
    + + diff --git a/flow/declarations.js b/flow/declarations.js index 942aa73bf..731c3983d 100644 --- a/flow/declarations.js +++ b/flow/declarations.js @@ -91,6 +91,7 @@ declare type Location = { declare type RawLocation = string | Location declare type Route = { + _force?: boolean; path: string; name: ?string; hash: string; diff --git a/src/history/base.js b/src/history/base.js index 5b6f199c6..6260d3978 100644 --- a/src/history/base.js +++ b/src/history/base.js @@ -97,8 +97,9 @@ export class History { } onAbort && onAbort(err) } - if ( - isSameRoute(route, current) && + if (isSameRoute(route, current) && + // case for public function "reload" + !route._force && // in the case the route map has been dynamically appended to route.matched.length === current.matched.length ) { diff --git a/src/index.js b/src/index.js index a1a7b8507..05dcc3c42 100644 --- a/src/index.js +++ b/src/index.js @@ -142,6 +142,17 @@ export default class VueRouter { this.history.push(location, onComplete, onAbort) } + reload () { + const { current } = this.history + const result: Object = { _force: true } + if (current.hash) result.hash = current.hash + if (current.path) result.path = current.path + if (current.params) result.params = current.params + if (current.query) result.query = current.query + + this.history.push(result) + } + replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.history.replace(location, onComplete, onAbort) } diff --git a/src/util/location.js b/src/util/location.js index 364e6fc0c..64d5d64a7 100644 --- a/src/util/location.js +++ b/src/util/location.js @@ -53,10 +53,16 @@ export function normalizeLocation ( hash = `#${hash}` } - return { + const result: Object = { _normalized: true, path, query, hash } + // check force + if (next._force) { + result._force = true + } + + return result } diff --git a/src/util/route.js b/src/util/route.js index 54a91a738..02a35c37c 100644 --- a/src/util/route.js +++ b/src/util/route.js @@ -31,6 +31,9 @@ export function createRoute ( if (redirectedFrom) { route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery) } + if (location._force) { + route._force = true + } return Object.freeze(route) }