Routing 
WinJS applications are single-page applications, where page navigation is completed on the browser side without requesting HTML from the server again. HTML is only loaded once during application initialization. All pages are composed of different components, and page switching is actually switching between different components. You just need to associate different route paths with corresponding components in the configuration.
Route Type Configuration 
Please refer to history configuration.
Configuring Routes 
Configure routes through routes in the configuration file, formatted as an array of route information.
For example:
// .winrc.ts
export default {
  routes: [
    { path: '/', component: 'index' },
    { path: '/user', component: 'user' },
  ],
}WinJS splits bundles by page by default for faster page loading speeds. Since the loading process is asynchronous, you often need to write or reference something like nprogress to add loading styles to your project and improve the experience.
Tip
You can set the network to slow speed in Chrome Devtools > Network tab, then switch routes to check if component loading is working.
path 
- Type: 
string 
path only supports two types of placeholder configurations: the first is dynamic parameters in the form of :id, and the second is * wildcards, which can only appear at the end of the route string.
✅ The following are currently supported route path configuration forms:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*❌ The following are currently not supported route path configuration forms:
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*name 
Named routes
Besides path, you can also provide a name for any route:
export default {
  routes: [
    {
      path: '/user/:username',
      name: 'user',
      component: 'index'
    }
  ]
}To link to a named route, you can pass an object to the router-link component's to property:
<router-link :to="{ name: 'user', params: { username: 'erina' }}">
  User
</router-link>This has the same effect as calling router.push imperatively:
router.push({ name: 'user', params: { username: 'erina' } })Both methods can navigate to the path /user/erina.
alias 
A redirect means that when a user visits /home, the URL will be replaced by /, and then matched as /. So what is an alias?
Aliasing / as /home means that when a user visits /home, the URL remains /home, but it will be matched as if the user is visiting /.
The corresponding route configuration is:
export default {
  routes: [
    {
      path: '/',
      component: 'index',
      alias: '/home'
    }
  ]
}Through aliases, you can freely map UI structure to any URL without being constrained by the nested structure of the configuration. Make aliases start with / to make paths in nested routes absolute paths. You can even combine both and provide multiple aliases with an array:
export default {
  routes: [
    {
      path: '/users',
      component: 'users',
      routes: [
        // Render UserList for these 3 URLs
        // - /users
        // - /users/list
        // - /people
        { path: '', component: '/users/UserList', alias: ['/people', 'list'] },
      ]
    }
  ]
}meta 
When defining routes, we can configure the meta field to extend route meta information and record some route-related information:
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      routes: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: {
            title: 'bar',
            requiresAuth: true
          },
        },
      ],
    },
  ],
});component 
- Type: 
string 
Configure the React component path used for rendering after location and path matching. It can be an absolute path or a relative path. If it's a relative path, it will start searching from src/pages.
If pointing to a file in the src directory, you can use @, for example component: '@/layouts/basic'. It's recommended to use @ to organize route file locations.
routes 
Configure child routes, usually used when you need to add layout components for multiple paths.
For example:
export default {
  routes: [
    { path: '/login', component: 'login' },
    {
      path: '/',
      component: '@/layouts/index',
      routes: [
        { path: '/list', component: 'list' },
        { path: '/admin', component: 'admin' },
      ],
    },
  ],
}In the global layout src/layouts/index, render child routes through <router-view />:
export default function Page() {
  return (
    <div style={{ padding: 20 }}>
      <router-view />
    </div>
  )
}This way, accessing /list and /admin will include the src/layouts/index layout component.
redirect 
- Type: 
string 
Configure route redirection.
For example:
export default {
  routes: [
    { path: '/', redirect: '/list' },
    { path: '/list', component: 'list' },
  ],
}Accessing / will redirect to /list and be rendered by the src/pages/list file.
The redirect target can also be a named route. The following example redirects from /home to /:
export default {
  routes: [
    {
      path: '/home',
      redirect: {
        name: 'homepage'
      }
    }
  ]
}wrappers 
- Type: 
string[] 
Configure wrapper components for route components. Wrapper components can combine more functionality into the current route component. For example, it can be used for route-level permission verification:
export default {
  routes: [
    {
      path: '/user', component: 'user',
      wrappers: [
        '@/wrappers/auth',
      ],
    },
    { path: '/login', component: 'login' },
  ]
}Then in src/wrappers/auth,
export default (props) => {
  const { isLogin } = useAuth();
  if (isLogin) {
    return <router-view />;
  } else {
    return <router-link to="/login" />;
  }
}This way, when accessing /user, permission verification is performed through the auth component. If passed, src/pages/user is rendered, otherwise it redirects to /login.
Note
Each component in wrappers will add a nested route layer to the current route component. If you want the route structure to remain unchanged, it's recommended to use Higher-Order Components (HOC). First implement the wrapper logic in the HOC, then use the HOC to decorate the corresponding route component.
Example:
// src/hocs/withAuth.tsx
const withAuth = (Component) => () => {
  const { isLogin } = useAuth();
  if (isLogin) {
    return <router-view />;
  } else {
    return <router-link to="/login" />;
  }
}// src/pages/user.tsx
const TheOldPage = () => {
  // ...
}
export default withAuth(TheOldPage)Conventional Routing 
When using conventional routing, all
(j|t)sx?files undersrc/pagesare treated as routes. If you need to modify the default rules, you can use the conventionRoutes configuration.
In addition to configured routing, WinJS also supports conventional routing. Conventional routing, also known as file-based routing, means you don't need to write configuration manually. The file system becomes the routes, and route configuration is analyzed from directory and file structure and naming, ultimately generating the corresponding route structure.
If there is no routes configuration, WinJS will enter conventional routing mode, then analyze the src/pages directory to get the route configuration.
For example, the following file structure:
.
  └── pages
    ├── index.tsx
    └── users.tsxWill generate the following route configuration:
[
  { path: '/', name: 'index', component: '@/pages/index' },
  { path: '/users', name: 'users', component: '@/pages/users' },
]Configuring Extended Properties 
We can set extended properties for pages in two ways, allowing conventional routing to read custom page properties such as meta.
- Using the 
definePageMetamethod (Recommended) 
definePageMeta(meta: PageMeta) => meta
interface PageMeta {
  name?: string
  alias?: string | string[]
  [key: string]: unknown
}- Using the 
routePropsproperty 
Example:
<script setup>
  import { ref } from 'vue';
  import { definePageMeta } from 'winjs';
 
  // You can get the currently set meta field in the route interceptor (router.beforeEach)
  
  // 1. Recommended: use definePageMeta()
  definePageMeta({
    meta: {
      title: 'pageMeta'
    }
  })
 
  // 2. export const routeProps
  export const routeProps = {
    meta: { requireAuth: true }
  }
  
  const title = ref('Welcome to Your Vue.js App.');
</script>
<template>
  <div class="page page-hello">
    <div class="page-content">
      <!-- Example of static asset path -->
      <img class="logo" src="@/assets/img/logo.png" alt="logo" />
    </div>
  </div>
</template>Note
The above solution only works in conventional routing mode. That is, when it is detected that the WinJS configuration file .winrc does not contain the routes property, this feature will be automatically enabled.
Dynamic Routes 
By convention, directories or files with the $ prefix are dynamic routes. If no parameter name is specified after $, it represents * wildcard. For example, the following directory structure:
For example:
src/pages/users/$id.tsxwill become/users/:idsrc/pages/users/$id/settings.tsxwill become/users/:id/settings
A complete example, with the following file structure:
+ pages/
  + foo/
    - $slug.tsx
  + $bar/
    - $.tsx
  - index.tsxWill generate the following route configuration:
[
  { path: '/', name: 'index', component: '@/pages/index.tsx' },
  { path: '/foo/:slug', name: 'fooSlug', component: '@/pages/foo/$slug.tsx' },
  { path: '/:bar/*', name: 'bar', component: '@/pages/$bar/$.tsx' }
];Global Layout 
By convention, src/layouts/index.tsx is the global layout. It returns a Vue component and renders nested routes through <router-view />.
With the following directory structure:
.
└── src
    ├── layouts
    │   └── index.tsx
    └── pages
        ├── index.tsx
        └── users.tsxWill generate the following routes:
[
  {
    path: '/',
    component: '@/layouts/index',
    routes: [
      { path: '', name: 'index', component: '@/pages/index' },
      { path: 'users', name: 'users', component: '@/pages/users' },
    ],
  },
]You can use layout: false to fine-tune and disable the global layout display for a specific route. This option only takes effect at the first level:
  routes: [
  {
    path: '/',
    name: 'index',
    component: './index',
    // 🟢 Works
    layout: false
  },
  {
    path: '/users',
    name: 'users',
    routes: [
      // 🔴 Does not work, at this point the layout of this route is not the global layout, but `/users`
      { layout: false }
    ]
  }
]A custom global layout format looks like this:
export default function Layout() {
  return <router-view />
}Different Global Layouts 
You may need to output different global layouts for different routes. WinJS does not support such configuration, but you can still differentiate location.path in src/layouts/index.tsx and render different layouts.
For example, if you want to output a simple layout for /login:
export default defineComponent(() => {
  setup()
  {
    const route = useRoute();
    // Use `useAppData` to get more route information
    // const { clientRoutes } = useAppData()
    if (route.path === '/login') {
      return () => (
        <div>
          <SimpleLayout>
            <router-view />
          </SimpleLayout>
        </div>
      )
    }
    return () => (
      <div>
        <Header />
        <router-view />
        <Footer />
      </div>
    );
  }
});Nested Routes 
Although in WinJS, conventional routing does not support configuring different global layouts, we can create nested routes through the "directory convention" below, where both the current directory and subdirectories are child routes.
Assuming the directory structure of src/pages is as follows:
.
├── docs.vue
├── hello
│   ├── child.vue
│   └── style.less
├── hello.vue
└── index.vueNote
The folder and current-level route should have the same name.
./src/pages
├── ${name}
│   └── a.vue[tsx]
└── ${name}.vue[tsx]Among them, the example code in hello.vue is:
<template>
  <div class="page page-hello">
    <h3>Hello parent</h3>
    <router-view />
  </div>
</template>Then, the route configuration automatically generated by WinJS is as follows:
{
  "hello": {
    "path": "hello",
    "id": "hello",
    "name": "hello",
    "parentId": "@@/global-layout"
  },
  "hello/child": {
    "path": "child",
    "id": "hello/child",
    "name": "helloChild",
    "parentId": "hello"
  },
  "index": {
    "path": "/",
    "id": "index",
    "name": "index",
    "parentId": "@@/global-layout"
  },
  "docs": {
    "path": "docs",
    "id": "docs",
    "name": "docs",
    "parentId": "@@/global-layout"
  },
  "@@/global-layout": {
    "id": "@@/global-layout",
    "path": "/",
    "isLayout": true
  }
};404 Route 
By convention, src/pages/404.tsx is the 404 page and must return a Vue component. The default value of name is NotFound.
For example, the following directory structure:
.
└── pages
    ├── 404.tsx
    ├── index.tsx
    └── users.tsxWill generate routes:
[
  { path: '/', name: 'index', component: '@/pages/index' },
  { path: '/users', name: 'users', component: '@/pages/users' },
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: '@/pages/404' },
]In this way, if you access /foo, neither / nor /users can match, it will fallback to the 404 route and be rendered through src/pages/404.tsx.
404 only takes effect automatically with conventional routing. If using configured routing, you need to configure the 404 wildcard route yourself.
Tip
Different versions of vue-router, 4.x and 3.x will have differences in the generated 404-related code. For 4.x, refer to 404 Route. For 3.x, refer to 404 Route.
4.x corresponds to Vue3, and the generated code is as follows:
[
  { path: '/', name: 'index', component: '@/pages/index' },
  { path: '/users', name: 'users', component: '@/pages/users' },
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: '@/pages/404' },
]2.x corresponds to Vue2, and the generated code is as follows:
[
  { path: '/', name: 'index', component: '@/pages/index' },
  { path: '/users', name: 'users', component: '@/pages/users' },
  // Must be placed at the last position in the route table, otherwise any route access will enter 404
  { path: '/*', name: 'NotFound', component: '@/pages/404' },
]Page Navigation 
For programmatic navigation or within components, please use the useRouter API
<script lang="ts" setup>
  import { useRouter, useRoute } from 'winjs';
  const router = useRouter()
  const route = useRoute()
  const onHello = () => {
    router.push({
      name: 'search',
      query: {
        ...route.query,
      },
    })
  }
  // These three forms are equivalent
  router.push('/users/posva#bio');
  router.push({ path: '/users/posva', hash: '#bio' });
  router.push({ name: 'users', params: { username: 'posva' }, hash: '#bio' });
  // Only change hash
  router.push({ hash: '#bio' });
  // Only change query
  router.push({ query: { page: '2' } });
  // Only change param
  router.push({ params: { username: 'jolyne' } });
  // Navigate to the previous route
  router.back();
  // Navigate to the forward history record
  router.go(1);
  // Replace the record in the history stack
  router.replace('/new');
</script>For more details, see here
router-link 
router-view 
RouterLink Component 
For example:
export default function Page () {
  return (
    <div>
      <router-link to="/users">Users Page</router-link>
    </div>
  )
}Then clicking Users Page will navigate to the /users address.
Note:
RouterLinkis only used for internal navigation within a single-page application. For external URL navigation, please use theatag
Runtime Configuration 
You can control vue and vue-router related configuration through export in the conventional src/app.tsx
router 
Configure router configuration
// src/app.tsx
export const router: RouterConfig = {
  // @ts-ignore
  scrollBehavior(to, from) {
    console.log('scrollBehavior', to, from);
  },
};onMounted({app, router}) 
Vue app mount success callback. Here you can get the app instance and router instance, and can register global components, route interceptors, etc.
export function onMounted({ app, router }: any) {
  console.log('onMounted', app, router);
  app.provide('win-hello', {
    h: 'hello',
    w: 'word',
  });
}rootContainer(container) 
Modify the root component when handing over to vue-router for rendering.
For example, to wrap an outer parent component
import { h } from 'vue'
export function rootContainer(container) {
  return h(ThemeProvider, null, container);
}