Routing
WinJS applications aresingle-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 tohistory configuration.
Configuring Routes
Configure routes throughroutes in the configuration file, formatted as an array of route information.
For example:
// .winrc.tsexport 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 likenprogress 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 currentlysupported route path configuration forms:
/groups/groups/admin/users/:id/users/:id/messages/files/*/files/:id/*❌ The following are currentlynot supported route path configuration forms:
/users/:id?/tweets/:id(\d+)/files/*/cat.jpg/files-*name
Named routes
Besidespath, you can also provide aname 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 therouter-link component'sto property:
<router-link :to="{ name: 'user', params: { username: 'erina' }}"> User</router-link>This has the same effect as callingrouter.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 themeta 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 fromsrc/pages.
If pointing to a file in thesrc directory, you can use@, for examplecomponent: '@/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 layoutsrc/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 thesrc/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 thesrc/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 insrc/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 theauth component. If passed,src/pages/user is rendered, otherwise it redirects to/login.
Note
Each component inwrappers 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.tsxconst withAuth = (Component)=> ()=> { const {isLogin }= useAuth(); if (isLogin) { return <router-view />; }else { return <router-link to="/login" />; }}// src/pages/user.tsxconst 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 theconventionRoutes 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 thesrc/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, allowingconventional routing to read custom page properties such asmeta.
- Using the
definePageMetamethod (Recommended)
definePageMeta(meta: PageMeta)=> metainterface 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 inconventional routing mode. That is, when it is detected that the WinJS configuration file.winrc does not contain theroutes 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 uselayout: false to fine-tune and disable theglobal 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 globallayout 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 differentiatelocation.path insrc/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 inhello.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 ofname isNotFound.
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 throughsrc/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 to404 Route. For 3.x, refer to404 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 theuseRouter 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 clickingUsers 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 conventionalsrc/app.tsx
router
Configure router configuration
// src/app.tsxexport 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);}