-
-
Notifications
You must be signed in to change notification settings - Fork 5k
[Feature Request] Add support for Vue 2.7 #3760
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
Comments
I think it makes sense to release this as a new minor. It would also be the last minor we release in Vue Router to align with Vue Core. |
@posva Should we change source code to typescript align to vue 2.7? |
no, because this codebase is not going to get the upgrade vue (adding a whole set of new APIs). Vue Router v4 composables should be exposed as a sub import: |
Ok, can I take this? I'm willing to create a PR about vue 2.7. |
Of course! |
I made a 2.7 compatible version. Use it as follows: import { useRouter } from '@logue/vue2-helpers/vue-router'; When officially supported, you can use it as it is by deleting |
@psalaets much expect! |
You can do this! // utils.js
import { getCurrentInstance } from 'vue'
export function useRoute() {
const { proxy } = getCurrentInstance()
const route = proxy.$route
return route
}
export function useRouter() {
const { proxy } = getCurrentInstance()
const router = proxy.$router
return router
} import { useRouter, useRoute } from '@/utils/index.js' // '@/utils/index.js' => 'vue-router'
setup() {
const router = useRouter()
const route = useRoute()
// ...
} |
You can use it temporarily: // in router/index
import { computed, reactive } from 'vue'
import VueRouter from 'vue-router'
let Router = null
export function setupRouter(app) {
app.use(VueRouter)
Router = new VueRouter({
mode: 'history',
})
// Router === reactive(Router) reactive use Vue.observable
reactive(Router)
return Router
}
export function useRoute() {
const routeEffect = computed(() => Router?.currentRoute || {})
/**
* or
*/
return {
...(Router?.currentRoute || {}),
__effect__: routeEffect, // is ref
}
/**
* or
*/
// return { route: Router?.currentRoute, routeEffect };
/**
* use in setup
* const { route, routeEffect } = useRoute()
* console.log(route); // { fullPath: '/', name: null, ...}
* console.log(routeEffect); // { effect: {...}, value: {...} }
*/
/**
* or
* If you don't mind using .value
*/
// return routeEffect
} or vuex: import Vuex from 'vuex'
const Store = new Vuex.Store({})
export function useStore() {
return Store
} and use : <script setup>
import { computed } from 'vue'
import { useRouter, useRoute } from '@/router'
import { useStore } from '@/store'
const store = useStore()
const router = useRouter()
const route = useRoute()
const currentRouteName = computed(() =>route.__effect__.value.name)
</script> |
@Lupeiwen0 One of the reasons for wanting to have this is the ability to watch for changes in the current route object (parameters, query parameters, etc) from the active component. |
I just wanted to write this feature, and you guys have already done it. It's exactly what I thought it would be. Ha ha. |
@ElteHupkes |
I took a look at your implementation, and the responsiveness is still a bit of a problem. So I proposed a PR of my own. |
Inspired by source code of v4, I write the composable like this: Effect:
For Js:// @/router/useApi.js
import { reactive, shallowRef, computed } from 'vue'
/**
* @typedef { import("vue-router").default } Router
* @typedef { import("vue-router").Route } Route
*/
/**
* vue-router composables
* @param {Router} router - router instance
*/
export function useApi(router) {
const currentRoute = shallowRef({
path: '/',
name: undefined,
params: {},
query: {},
hash: '',
fullPath: '/',
matched: [],
meta: {},
redirectedFrom: undefined,
})
/** @type {Route} */
const reactiveRoute = {}
for (const key in currentRoute.value) {
reactiveRoute[key] = computed(() => currentRoute.value[key])
}
router.afterEach((to) => {
currentRoute.value = to
})
/**
* get router instance
*/
function useRouter() {
return router
}
/**
* get current route object
*/
function useRoute() {
return reactive(reactiveRoute)
}
return {
useRouter,
useRoute
}
} For Ts:// @/router/useApi.ts
import { reactive, shallowRef, computed } from 'vue'
import type { default as VueRouter, Route } from 'vue-router'
export function useApi(router: VueRouter) {
const currentRoute = shallowRef<Route>({
path: '/',
name: undefined,
params: {},
query: {},
hash: '',
fullPath: '/',
matched: [],
meta: {},
redirectedFrom: undefined,
})
const reactiveRoute = {} as Route
for (const key in currentRoute.value) {
// @ts-expect-error: the key matches
reactiveRoute[key] = computed(() => currentRoute.value[key])
}
router.afterEach((to) => {
currentRoute.value = to
})
function useRouter() {
return router
}
function useRoute() {
return reactive(reactiveRoute)
}
return {
useRouter,
useRoute
}
} usage:// @/router/index.js
import Router from 'vue-router'
import { useApi } from './useApi'
const router = new Router({
// ...
})
const { useRoute, useRouter } = useApi(router)
export default router
export { useRoute, useRouter } |
@Lphal Thanks, that looks similar to something I ended up with, and a bit less messy. Two things to pay attention to when using this:
|
One more problem should be adressed to support vue 2.7 with typescript. When using component as normal import (not async import), vue-tsc throws error. App.vue <template />
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({});
</script> routes.ts import {RouteConfig} from "vue-router"
import App from "./App.vue";
const routes: RouteConfig[] = [
{
path: "/my/orders/",
name: "OrdersTable",
component: App,
},
];
export default routes;
Full example here. This could be fixed by adding type Component = ComponentOptions<Vue> | typeof Vue | AsyncComponent | DefineComponent |
Hi! When will this minor version be released? @posva |
I copy/pasted the code from the ongoing PR with success as a temporary workaround in my app ;) Thanks everyone 👍 |
I've been using something that is very similar to @Lphal snippet! // if using 2.6.x import from @vue/composition-api
import { computed, reactive, getCurrentInstance } from 'vue'
export function useRouter() {
return getCurrentInstance().proxy.$router
}
export function useRoute() {
const currentRoute = computed(() => getCurrentInstance().proxy.$route)
const protoRoute = Object.keys(currentRoute.value).reduce(
(acc, key) => {
acc[key] = computed(() => currentRoute.value[key])
return acc
},
{}
)
return reactive(protoRoute)
} Didn't do the most extensive testing but you can cover the basics with this vue-router-migration-utils.spec.jsimport { createLocalVue } from '@vue/test-utils'
import { mountComposable } from '@/spec/helpers/mountComposable.js'
import { useRouter, useRoute } from '@/src/vue3-migration/vue-router.js'
import VueRouter from 'vue-router'
// remove if vue2.7
import VueCompositionAPI from '@vue/composition-api'
const localVue = createLocalVue()
localVue.use(VueRouter)
// remove if vue2.7
localVue.use(VueCompositionAPI)
// [1] https://lmiller1990.github.io/vue-testing-handbook/vue-router.html#writing-the-test
function createRouter() {
return new VueRouter({
mode: 'abstract',
routes: [
{
path: '/',
name: 'index',
meta: { title: 'vue-router hooks index' }
},
{
path: '/home',
name: 'home',
meta: { title: 'vue-router hooks home' }
},
{
path: '*',
name: '404',
meta: { title: '404 - Not Found' }
}
]
})
}
function useVueRouterFactory() {
// original return values, not unRefd template ones that we get via wrapper.vm
let router, route
const wrapper = mountComposable(
() => {
router = useRouter()
route = useRoute()
},
{ localVue, router: createRouter() }
)
return { wrapper, router, route }
}
it('both router and route return values should be defined', () => {
const { router, route } = useVueRouterFactory()
expect(router).toBeDefined()
expect(route).toBeDefined()
})
it('route is reactive to router interaction', async () => {
const { route, router } = useVueRouterFactory()
expect(route.name).toBe(null) // see [1]
await router.push('/home')
expect(route.name).toBe('home')
expect(route.meta.title).toBe('vue-router hooks home')
expect(router.currentRoute.name).toBe('home')
await router.push('/route-that-does-not-exist')
expect(route.name).toBe('404')
expect(route.meta.title).toBe('404 - Not Found')
expect(router.currentRoute.name).toBe('404')
})
it('accepts global router guards callbacks', async () => {
const { router } = useVueRouterFactory()
const onAfterEach = jest.fn()
router.afterEach(onAfterEach)
expect(router.afterHooks.length).toBe(1)
await router.push('/')
expect(onAfterEach).toHaveBeenCalledTimes(1)
})
// meta testing, ensure that we're not leaking currentRoute to other assertions
// of this suite. see [1] if you remove "mode: abstract" this will fail.
// because route will be the last one from previous spec
it('it cleanups vue router afterwards between specs', () => {
const { route } = useVueRouterFactory()
expect(route.name).toBe(null)
}) |
For what it's worth, these solutions aren't sufficient if you're trying to use new APIs such as |
as a temporary fix for the issue with the const routes = RouteConfig[] = [
{
path: '/',
component: SomeComponent // oddly, I don't have this type error on parent routes...only the children
children: [
{
path: 'something',
component: AnotherComponent as any, // the workaround
}
]
}
] |
Any chance to get NavigationGuards to work with script setup in Vue 2.7? |
@posva this is great, is there any chance we can get |
@posva Thanks for the update! I've been trying to make it work with this configuration:
But getting some error messages in the build (see this thread) According to @YFengFly, probably it is a problem with webpack 4 (used by vue-cli 4). For the moment, I've managed to solve it using import { useRoute } from "vue-router/dist/composables" Not the most elegant way to do it, but it works. It would be interesting to adapt it so it woks cleanly as originally intended by importing from |
There are some issues with Webpack 4 since it doesn't support the |
It works ok now with version 3.6.4, ¡Gracias! |
This is driving me nuts. Using useRoute in App.vue and a comptued property like
|
@posva My project has not been upgraded to vue2.7, but I have used the library // vue-router/dist/composables.mjs
import { getCurrentInstance, effectScope, shallowReactive, onUnmounted, computed, unref } from 'vue';
// transform by Babel
import { getCurrentInstance, effectScope, shallowReactive, onUnmounted, computed, unref } from '@vue/composition-api'; is there any risk? |
I am curious why use shallowReactive instead of reactive? |
This is currently not working with:
Usage via composition API:
|
@woahitsjc do you have an example repo for this? |
@dennybiasiolli I tried to setup an example repo but couldn't replicate the issue. Although, I managed to find the cause of the problem. It was using a package called To get the store now, I'm doing:
|
What problem does this feature solve?
As composition api has been incoporated in vue 2.7,
vue-router
should provideuseRoute
anduseRouter
to enable router usage in<setup script>
.What does the proposed API look like?
Just like in Vue3
The text was updated successfully, but these errors were encountered: