-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to use vue-router (this.$router/$route) in the new function-based API? #70
Comments
@beeplin Good question. I've been wondering whether, when using this new API, it is preferable to use everything as a hooks:
I assume |
I don't know if there will be a way to inject things to context but the |
Currently in setup(props, context) {
const path = computed(() => context.root.$route.path)
...
} And I agree it would be better to have something like |
I'm with @aztalbot I think. We can provide functions to provide the router as well as inject it in components where needed: import { provideRouter, useRouter } from 'vue-router'
import router from './router'
new Vue({
setup() {
provideRouter(router)
}
})
// ... in a child component
export default {
setup() {
const { route /*, router */ } = useRouter()
const isActive = computed(() => route.name === 'myAwesomeRoute')
return {
isActive
}
}
}
|
Alternatively to 1., we could have the router object expose the "hook": import router from './router'
new Vue({
setup() {
router.use()
}
}) |
This will "break" every existing plugin I think, since you cannot access "this.$plugin" directly anymore. They'll all have to update themselves to use the new API and I don't know how exactly that would work after you install it with options. You can't just import them from the lib right? |
It will only "break" insofar as as plugins that don't yet provide function for use in Since Vue 3 is a major release that comes with other breaking changes, many plugins will have to update either way to stay compatible, and for others, upgrading to work with Getting back from "plugins" as a whole to the router in particular, I feel that the Vue 3 release would be a good moment to update the API and get rid of the prototype properties that we don't consider ideal anymore, especially since we will have support for
I'm not sure what you are referring to here. |
I could imagine something like this: // App.vue
export default {
setup() {
const { route, router } = initRouter({ routes, ...otherOptions });
},
}
// MyComponent.vue
export default {
setup() {
const { route, router } = inject(ROUTER_SERVICE);
// or
const { route, router } = useRouter();
},
}
// router.ts
export const ROUTER_SERVICE: Key<RouterService> = Symbol();
const DEFAULT_OPTIONS = {
serviceKey: ROUTER_SERVICE,
}
export function initRouter(options) {
options = mergeOptions(options, DEFAULT_OPTIONS);
const router = new Router(options);
const route = router.currentRoute;
const service = { router, route };
provide(options.serviceKey, service);
return service;
}
export function useRouter(serviceKey = ROUTER_SERVICE) {
return inject(serviceKey);
} |
I was referring to plugins that add options to the instance, like Vue SweetAlert. With the new api, those plugins would have to allow you to do |
@phiter Well, Vue 3 will have a plugin API as well. Following the proposal #29, mounting an app will work a wee bit differently, but we still have a So let's compare before/after: A plugin like SweetAlert would usually have an install function like this in vue 2: export default function install ( Vue, options) {
const swal = doSpoemthingWithOptions(options)
Vue.prototype.$swal = swal
} This is nice and short but has the not so optimal consequence that the prototype gets littered with properties that people try to namespace with In Vue 3, this would work something like this, better ideas notwithstanding: import { provide, inject } from 'vue'
const key = new Symbol('swal')
export default function install (app, options) {
const swal = doSomethingWithOptions(options)
// using a global mixins here to add a global setup().
// maybe we can have an `app.setup()` shortcut?
// #29 was written long before we came up with that new API
app.mixin({
setup() {
provide(key, swal)
}
})
}
export function useSwal() {
return inject(key)
} Usage: // setup
import { createApp } from 'vue'
import App from './App.vue'
import VueSweetAlert from 'vue-sweet-alert'
const app = createApp(App)
app.use(VueSweetAlert, { /* some options */})
app.mount('#app') // in component:
import { useSwal } from 'vue-sweet-alert'
export default {
setup() {
return {
swal: useSwal()
}
}
} Now, this may seem a bit more verbose, and it is, but it also is more explicit as well as easier to type in TS, it uses Vue's dependency injection ( Now you still might feel that you do want to inject this into every component because you use it so regularly. In that case, you could do this instead: app.mixin({
setup() {
return {
$swal: useSwal()
}
}
}) and last but not least don't forget that extending the prototype is still possible. you just won't be able to access those properties from within |
My current approach (to avoid disrupting the codebase too much) is to have the standard "2.x" router setup and then use this: export function useRouter(context: Context) {
const router = (<any>context.root).$router as VueRouter;
const copyRoute = (r: Route) => ({
name: r.name,
path: r.path,
fullPath: r.fullPath,
params: cloneDeep(r.params),
});
const route = state(copyRoute(router.currentRoute));
watch(
() => {
return (<any>context.root).$route;
},
r => {
Object.assign(route, copyRoute(r));
},
);
return {
router,
route,
};
} I use it in my setup(props, context) {
const { router, route } = useRouter(context);
const isHome = computed(() => route.name === 'home');
return { isHome };
} The |
By using a value() instead of state you don't need the copy. export function useRouter(context: Context) {
const router = (<any>context.root).$router as VueRouter;
const route = value(router.currentRoute);
watch(
() => {
return (<any>context.root).$route;
},
r => {
route.value = r
},
);
return {
router,
route,
};
} |
I tried that @LinusBorg but I get a strange error:
I believe it's the Also a user would then have to use An aside, for the solutions using (perhaps all of this should go in the plugin repo instead of here) |
probably |
Hello, import Vue from 'vue'
import VueRouter from 'vue-router'
import { provide, inject, reactive } from '@vue/composition-api'
Vue.use(VueRouter)
const router = new VueRouter({ /* ... */ })
const RouterSymbol = Symbol()
export function provideRouter() {
provide(RouterSymbol, reactive(router))
}
export function useRouter() {
const router = inject(RouterSymbol)
if (!router) {
// throw error, no store provided
}
return router as VueRouter
} If it can be of some help for anyone... |
I tried, and it work fine // composables/use-router.js
import { provide, inject } from '@vue/composition-api'
const RouterSymbol = Symbol()
export function provideRouter(router) {
provide(RouterSymbol, router)
}
export default function useRouter() {
const router = inject(RouterSymbol)
if (!router) {
// throw error, no store provided
}
return router
}
------------------
// App.vue
setup(props, { root: { $router} }) {
provideRouter($router)
------------
// Usage in component
import useRouter from './user-router'
export default () => {
const router = useRouter()
...... But don't work with |
You can't use inject in a function component if I remember correctly. You have to use an actual component. |
It |
That's not Javascript. I don't know what you want to say. |
Sorry my reply is sort.
$router.push(....)
$route.params |
My complete code to create injection of // composables/use-route.js
import { provide, inject } from '@vue/composition-api'
const RouteSymbol = Symbol()
export function provideRoute(route) {
provide(RouteSymbol, route)
}
export default function useRoute() {
const route = inject(RouteSymbol)
if (!route) {
// throw error, no store provided
}
return route
}
------------------
// App.vue
export default () => {
setup(props, { root: { $route} }) {
provideRoute($route)
------------
// Usage in component
import useRoute from './user-route'
export default () => {
setup(){
const route = useRoute()
console.log(route)
......
}
--------------- Result -----------
name: null
meta: {}
path: "/"
hash: ""
query: {}
params: {}
fullPath: "/"
matched: [] Don't work, my route {
path: '/login',
name: 'login',
component: () => import('../../ui/pages/Login.vue'),
meta: {
layout: 'Public',
},
}, |
@LinusBorg are your examples from #70 (comment) still valid?
is there a way to only hook into the setup of the App component? |
@backbone87 If you mean implicitly, I don't know how. For Vue 2.x plugins, I usually check if Now that plugins apply at app-level, not globally, it would be nice if we could hook into app "lifecycle hooks", e.g. |
A simple alternative to the current api. // hooks/use-router.ts
import { computed, getCurrentInstance } from '@vue/composition-api';
export const useRouter = () => {
const vm = getCurrentInstance();
if (!vm) {
throw new ReferenceError('Not found vue instance.');
}
const route = computed(() => vm.$route);
return { route, router: vm.$router } as const;
}; |
@negezor you don't even need the computed. Just go for a getter: return {
get route() { return vm.$route },
router: vm.$router
} Bonus chatter: there are differences between these 2 approaches:
|
@jods4 I chose computed for just one reason:
|
@negezor what's the difference with the getter? |
@jods4 In two cases:
setup() {
const { route } = useRouter();
return { route }
} |
Extracting the value is not reactive. return {
get route() { return vm.$route },
router: vm.$router
} |
UPDATE: My apologies, I was directed to the @negezor This sandbox example no longer works as of Aug 15. |
I used to use |
After
Vue.use(VueRouter)
shouldroute
androuter
be injected intocontext
insetup(props, context)
?The text was updated successfully, but these errors were encountered: