Skip to content

Improve hmr development experience #617

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions client.d.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
declare module 'vue-router/auto-routes' {
import type { RouteRecordRaw, Router } from 'vue-router'
import type { RouteRecordRaw, Router, RouterOptions } from 'vue-router'

/**
* Array of routes generated by unplugin-vue-router
*/
export const routes: RouteRecordRaw[]

type HandleHotUpdateCallback = (newRoutes: RouteRecordRaw[]) => void

/**
* Setups hot module replacement for routes.
* @param router - The router instance
* @param hotUpdateCallback - Callback to be called after replacing the routes and before the navigation
* @example
* Creates a router instance.
* @param options - The router options
* @param hotUpdateCallback - Callback to be called after replacing the routes and before the navigation.
* (If `import.meta.hot` is not supported, it will only be executed once during initialization.)
* @example Common usage
* ```ts
* import { createRouter, createWebHistory } from 'vue-router'
* import { routes, handleHotUpdate } from 'vue-router/auto-routes'
* import { routes, createRouter } from 'vue-router/auto-routes'
*
* const router = createRouter({
* history: createWebHistory(),
* routes,
* })
* if (import.meta.hot) {
* handleHotUpdate(router)
* }
* ```
*
* @example Hot module replacement
* ```ts
* import { createRouter, createWebHistory } from 'vue-router'
* import { routes, createRouter } from 'vue-router/auto-routes'
*
* const router = createRouter(
* {
* history: createWebHistory(),
* routes,
* },
* (newRoutes) => {
* console.log('🔥 HMR with', newRoutes)
* }
* )
* ```
*/
export function handleHotUpdate(
router: Router,
hotUpdateCallback?: (newRoutes: RouteRecordRaw[]) => void
): void
export function createRouter(
options: RouterOptions,
hotUpdateCallback?: HandleHotUpdateCallback
): Router
}

declare module 'vue-router' {
Expand Down
48 changes: 13 additions & 35 deletions docs/guide/hmr.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,32 @@

When using `definePage()` and `<route>` blocks, it's possible to enable Hot Module Replacement (HMR) for your routes **and avoid the need of reloading the page or the server** when you make changes to your routes.

Enabling HMR is **strongly recommended** and currently **only works with Vite**.

```ts [src/router.ts]
import { createRouter, createWebHistory } from 'vue-router'
import {
routes,
handleHotUpdate, // [!code ++]
} from 'vue-router/auto-routes'

export const router = createRouter({
history: createWebHistory(),
routes,
})

// This will update routes at runtime without reloading the page
if (import.meta.hot) { // [!code ++]
handleHotUpdate(router) // [!code ++]
} // [!code ++]
```
Enabling HMR is **strongly recommended** and currently **only works with Vite**. But you don't need to do anything, because this step has been done for you internally.

## Runtime routes

If you add routes at runtime, you will have to add them within a callback to ensure they are added during development.

```ts{16-23} [src/router.ts]
import { createRouter, createWebHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes'

export const router = createRouter({
history: createWebHistory(),
routes,
})
import { createWebHistory } from 'vue-router'
import { routes, createRouter } from 'vue-router/auto-routes'

export const router = createRouter(
{
history: createWebHistory(),
routes,
},
() => {
addRedirects()
}
)

function addRedirects() {
router.addRoute({
path: '/new-about',
redirect: '/about?from=/new-about',
})
}

if (import.meta.hot) {
handleHotUpdate(router, (newRoutes) => {
addRedirects()
})
} else {
// production
addRedirects()
}
```


Expand Down
10 changes: 3 additions & 7 deletions docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@ Given the following route configuration:
::: code-group

```ts [src/router.ts]
import { createRouter, createWebHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes' // [!code ++]
import { createRouter, createWebHistory } from 'vue-router' // [!code --]
import { createWebHistory } from 'vue-router/auto-routes' // [!code ++]
import { routes, createRouter } from 'vue-router/auto-routes' // [!code ++]

export const router = createRouter({
history: createWebHistory(),
Expand All @@ -209,11 +210,6 @@ export const router = createRouter({
] // [!code --]
routes, // [!code ++]
})

// This will update routes at runtime without reloading the page
if (import.meta.hot) { // [!code ++]
handleHotUpdate(router) // [!code ++]
} // [!code ++]
```

```ts{2,5} [main.ts]
Expand Down
27 changes: 12 additions & 15 deletions playground/src/router.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { createRouter, createWebHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes'
import { createWebHistory } from 'vue-router'
import { routes, createRouter } from 'vue-router/auto-routes'
import type { RouteRecordInfo, ParamValue } from 'vue-router'

export const router = createRouter({
history: createWebHistory(),
routes,
})
export const router = createRouter(
{
history: createWebHistory(),
routes,
},
(routes) => {
console.log('🔥 HMR', routes)
}
)

function addRedirects() {
router.addRoute({
Expand All @@ -14,15 +19,7 @@ function addRedirects() {
})
}

if (import.meta.hot) {
handleHotUpdate(router, (routes) => {
console.log('🔥 HMR with', routes)
addRedirects()
})
} else {
// production
addRedirects()
}
addRedirects()

// manual extension of route types
declare module 'vue-router/auto-routes' {
Expand Down
10 changes: 9 additions & 1 deletion src/core/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,19 @@ export function createRoutesContext(options: ResolvedOptions) {
)}\n`

let hmr = ts`
export function handleHotUpdate(_router, _hotUpdateCallback) {
import { createRouter as createVueRouter } from 'vue-router'

export function createRouter(options, _hotUpdateCallback) {
const _router = createVueRouter(options || {})

if (import.meta.hot) {
import.meta.hot.data.router = _router
import.meta.hot.data.router_hotUpdateCallback = _hotUpdateCallback
} else if (typeof _hotUpdateCallback === 'function') {
_hotUpdateCallback(_router.getRoutes())
}

return _router
}

if (import.meta.hot) {
Expand Down