vuejs / vue-router

🚦 The official router for Vue 2
http://v3.router.vuejs.org/
MIT License
18.99k stars 5.06k forks source link

Add support for custom history implementations #2052

Open ardoramor opened 6 years ago

ardoramor commented 6 years ago

What problem does this feature solve?

This feature would address many requests seen in relation to having greater control over applications history, stack management, initial route, and a few others.

There are several supported history modes. My proposal is to allow mode option accept an instance of a class that implements History API.

Instantiation would look like:

import { AbstractHistory } from 'vue-router'

class CustomHistory extends AbstractHistory {
   ...
}

const customHistory = new CustomHistory()

const router = new VueRouter({
  mode: customHistory,
  routes: [...]
})

This would give great flexibility to developers and would allow further integrations of cool features such as keeping reactive history with vuex.

What does the proposed API look like?

mode option would accept String or an instance of AbstractHistory. Internally, if an instance is detected, the history manipulation would happen through it.

bundyo commented 6 years ago

Disclaimer: I work for NativeScript Tooling

Using VueRouter in NativeScript-Vue

We want to use the default VueRouter in NativeScript-Vue since it is much more familiar to the casual web developer that comes to build native mobile apps than a custom solution of our own. However, currently the usage of VueRouter in NativeScript-Vue is hindered by the difference of how <router-view/> and {N} frame navigation works. <router-view/> essentially works, but directly replaces the content as it does in web, thus breaking frame animations and back button view caching. To work around this problem, we need to deeper changes to how the History is handled and the current VueRouter doesn't support custom History implementation.

However the above proposal while good is somewhat problematic. It is okay to be used directly in an app, but using this syntax to create an NPM package would include most of the VueRouter in the said package and the user should use the router from within that package, since importing it again would result in two routers, one of which without the said custom History in it.

Thus to work around this, our proposal is to use a factory to extend the VueRouter mode. Something in the lines of:

Init in VueRouter constructor

let CustomHistory
if (mode && mode.constructor === Object) {
  CustomHistory = mode.factory(AbstractHistory)

  mode = mode.name
}

if (!inBrowser && !CustomHistory) {
  mode = 'abstract'
}
this.mode = mode

switch (mode) {
  case 'history':
  ...
  default:
    if (CustomHistory) {
      this.history = new CustomHistory(this, options.base)
      break
    }
  ...
}

Factory module

export default (mode = {
  name: 'NativeScriptHistory',

  factory(baseClass) {
    return class NativeScriptHistory extends baseClass {
      constructor(router, base) {
        super(router, base)
        ...
      }

      ...
    }
  }
})

Usage

import { Vue, mode } from "nativescript-vue";  
import VueRouter from "vue-router";

Vue.use(VueRouter);  

const router = new VueRouter({
    mode,
    routes: [...],
});

With some modifications this can be reworked to inherit any of the existing histories.

What do you think?

I can make a pull request if needed.

jlooper commented 6 years ago

Kamen has made a fork of the Vue Router with changes we'd like to see implemented here https://github.com/nativescript-vue/nativescript-vue-router/pull/1 - for now, we can use this fork but going forward, of course, for maintainability, we'd love to have them incorporated into the proper Vue Router

jlooper commented 6 years ago

cc @posva @yyx990803 @bundyo @rigor789

yyx990803 commented 6 years ago

Personally, I've always thought that vue-router's design isn't perfectly suited for mobile development as well. Most web apps perform full content replacement on navigation, while mobile apps often uses a stack-like structure where the previous screens are held in cache and can be navigated back to.

I've always wanted to revisit the problem and see if it's possible to provide one solution for both use cases, but deep down I feel that it may be best to have a dedicated solution for each due to the fundamental difference in how navigation is modeled on different platforms.

I'd be curious to hear more details on how a custom history implementation would help solve this, although personally, I don't think it's the best route forward trying to bend vue-router to fit mobile development.

bundyo commented 6 years ago

You are right - most mobile apps are working on a stack like cache history structure, while on the web direct replacement takes place. Additionally mobile apps not always depend on CSS for the view animations (the current versions of NativeScript don't yet support such animations, though this may change). However the rest of the functionality in the router is shared and we also wanted the user to experience something he is already used to when coming from the web - a router-view, history management and data propagation between the routes. This was the main reason we decided to use VueRouter instead of writing a new one.

I probably want too much, but I see a future VueRouter that is more modular - the core functionality and data handling to be baked in and the router-view/link implementations, history management and transition handling to be replaceable, depending on the occasion. This will also allow for better web/mobile code sharing - even though the views on desktop web and mobile apps differ greatly - the views in the mobile web and mobile apps do not.

Currently we use the VueRouter by passing our own history, based on the AbstractHistory, which is only doing some argument checks and passing all arguments to NativeScript-Vue's Frame implementation which takes care of doing the navigation through the NativeScript history stack.

posva commented 6 years ago

I thought a custom router view implementation could deal with most of the issues related to the view stack. Adding that to custom history implementation should provide a nice starter ground to build nativescript On Mon 17 Sep 2018 at 13:48, Bundyo (Kamen Bundev) notifications@github.com wrote:

You are right - most mobile apps are working on a stack like cache history structure, while on the web direct replacement takes place. Additionally mobile apps not always depend on CSS for the view animations (the current versions of NativeScript don't yet support such animations, though this may change). However the rest of the functionality in the router is shared and we also wanted the user to experience something he is already used to when coming from the web - a router-view, history management and data propagation between the routes. This was the main reason we decided to use VueRouter instead of writing a new one.

I probably want too much, but I see a future VueRouter that is more modular - the core functionality and data handling to be baked in and the router-view/link implementations, history management and transition handling to be replaceable, depending on the occasion. This will also allow for better web/mobile code sharing - even though the views on desktop web and mobile apps differ greatly - the views in the mobile web and mobile apps do not.

Currently we use the VueRouter by passing our own history, based on the AbstractHistory, which is only doing some argument checks and passing all arguments to NativeScript-Vue's Frame implementation which takes care of doing the navigation through the NativeScript history stack.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vuejs/vue-router/issues/2052#issuecomment-421982090, or mute the thread https://github.com/notifications/unsubscribe-auth/AAoicU3n-iFcV1g2jdu72hPcBSW_QHtqks5ub4v_gaJpZM4R-W8Q .

--

Eduardo San Martin Morote

bundyo commented 6 years ago

I've tried to create a custom router-view, but since it is functional - I was unable to get to the Frame that houses it since I have to trigger the navigation there. We also thought about integrating the Frame inside the router-view - but this might be a point for confusion, since without the Router, one has to use it to do navigation.

But yes, if I can somehow can get to the Frame from inside a custom router-view and use custom history - that would be great and solve our issues.