Skip to content

Latest commit

 

History

History
1081 lines (785 loc) · 32.4 KB

api-reference.md

File metadata and controls

1081 lines (785 loc) · 32.4 KB

BestVue3 Router API Reference

BestVue3 Router is a collection of Vue3 components, Composition API(also known as hooks) and utilities that make it easy to build multi-page applications with Vue3. This reference contains the component props declaration, function signatures and return types of the various interfaces in BestVue3 Router.

[!Tip:]

Please refer to our examples for more in-depth usage examples of how you can use BestVue3 Router to accomplish specific tasks.

Overview

Install

BestVue3 Router is published to npm named @bv3/router:

npm i @bv3/router -S

or using yarn

yarn add @bv3/router

Setup

To get BestVue3 Router working in your app, you need to render a router element at or near the root of your element tree. We provide several different routers, depending on where you're running your app.

  • <BrowserRouter> or <HashRouter> should be used when running in a web browser. Which one you pick depends on the style of URL you prefer or need.
  • <StaticRouter> should be used when server-rendering a website
  • <MemoryRouter> is useful in testing scenarios and as a reference implementation for the other routers

These routers provide the context that BestVue3 Router needs to operate in a particular environment. Each one renders a <Router> internally, which you may also do if you need more fine-grained control for some reason. But it is highly likely that one of the built-in routers is what you need.

Routing

Routing is the process of deciding which BestVue3 elements will be rendered on a given page of your app, and how they will be nested. BestVue3 Router provides two interfaces for declaring your routes.

A few low-level pieces that we use internally are also exposed as public API, in case you need to build your own higher-level interfaces for some reason.

Navigation

BestVue3 Router's navigation interfaces let you change the currently rendered page by modifying the current location. There are two main interfaces for navigating between pages in your app, depending on what you need.

  • <Link> render an accessible <a> element. This lets the user initiate navigation by clicking an element on the page.
  • useNavigate and <Navigate> let you programmatically navigate, usually in an event handler or in response to some change in state

There are a few low-level APIs that we use internally that may also prove useful when building your own navigation interfaces.

  • useResolvedPath - resolves a relative path against the current location
  • useHref - resolves a relative path suitable for use as a <a href>
  • resolvePath - resolves a relative path against a given URL pathname

Confirming Navigation

Sometimes you need to confirm navigation before it actually happens. For example, if the user has entered some data into a form on the current page, you may want to prompt them to save the data before they navigate to a different page.

  • usePrompt and <Prompt> trigger a platform-native confirmation prompt when the user tries to navigate away from the current page
  • useBlocker is a low-level interface that lets you keep the user on the same page and execute a function that will be called when they try to navigate away

Search Parameters

Access to the URL search parameters is provided via the useSearchParams hook.


Reference

<BrowserRouter>

Props declaration
const BrowserRouterProps = {
    window: Object as PropType<Window>,
} as const

<BrowserRouter> is the recommended interface for running BestVue3 Router in a web browser. A <BrowserRouter> stores the current location in the browser's address bar using clean URLs and navigates using the browser's built-in history stack.

<BrowserRouter window> defaults to using the current document's defaultView, but it may also be used to track changes to another's window's URL, in an <iframe>, for example.

import { createApp } from 'vue'

createApp(() => (
    <BrowserRouter>{/* The rest of your app goes here */}</BrowserRouter>
)).mount('#app')

<HashRouter>

Props declaration
const HashRouterProps = {
    window: Object as PropType<Window>,
} as const

<HashRouter> is for use in web browsers when the URL should not (or cannot) be sent to the server for some reason. This may happen in some shared hosting scenarios where you do not have full control over the server. In these situations, <HashRouter> makes it possible to store the current location in the hash portion of the current URL, so it is never sent to the server.

<HashRouter window> defaults to using the current document's defaultView, but it may also be used to track changes to another window's URL, in an <iframe>, for example.

import { createApp } from 'vue'

createApp(() => (
    <HashRouter>{/* The rest of your app goes here */}</HashRouter>
)).mount('#app')

<MemoryRouter>

Props declaration
export const MemoryRouterProps = {
    initialEntries: {
        type: Array as PropType<InitialEntry[]>,
    },
    initialIndex: Number,
} as const

A <MemoryRouter> stores its locations internally in an array. Unlike <BrowserHistory> and <HashHistory>, it isn't tied to an external source, like the history stack in a browser. This makes it ideal for scenarios where you need complete control over the history stack, like testing.

  • <MemoryRouter initialEntries> defaults to ["/"] (a single entry at the root / URL)
  • <MemoryRouter initialIndex> defaults to the last index of props.initialEntries

[!Tip:]

Most of BestVue3 Router's tests are written using a <MemoryRouter> as the source of truth, so you can see some great examples of using it by just browsing through our tests.

import { mount } from '@vue/test-utils'

describe('My app', () => {
    it('renders correctly', () => {
        const wrapper = mount(() => (
            <MemoryRouter initialEntries={['/users/mjackson']}>
                <Routes>
                    <Route path="users" element={<Users />}>
                        <Route path=":id" element={<UserProfile />} />
                    </Route>
                </Routes>
            </MemoryRouter>
        ))

        expect(wrapper.html()).toMatchSnapshot()
    })
})

<Link>

Props declaration
const LinkProps = {
    onClick: {
        type: Function as PropType<(e: MouseEvent) => void>,
    },
    replace: Boolean,
    state: Object as PropType<State>,
    target: String,
    linkRef: Object as PropType<Ref<HTMLAnchorElement>>,
    to: {
        type: [String, Object] as PropType<To>,
        required: true,
    },
} as const

A <Link> is an element that lets the user navigate to another page by clicking or tapping on it. A <Link> renders an accessible <a> element with a real href that points to the resource it's linking to. This means that things like right-clicking a <Link> work as you'd expect.

import { Link } from '@bv3/router'

// functional component
function UsersIndexPage({ users }) {
    return (
        <div>
            <h1>Users</h1>
            <ul>
                {users.map(user => (
                    <li key={user.id}>
                        <Link to={user.id}>{user.name}</Link>
                    </li>
                ))}
            </ul>
        </div>
    )
}

A relative <Link to> value (that does not begin with /) resolves relative to the parent route, which means that it builds upon the URL path that was matched by the route that rendered that <Link>. It may contain .. to link to routes further up the hierarchy. In these cases, .. works exactly like the command-line cd function; each .. removes one segment of the parent path.

[!Note:]

<Link to> with a .. behaves differently from a normal <a href> when the current URL ends with /. <Link to> ignores the trailing slash, and removes one URL segment for each ... But an <a href> value handles .. differently when the current URL ends with / vs when it does not.

<Navigate>

Props declaration
const NavigateProps = {
    to: {
        type: [Object, String] as PropType<To>,
        required: true,
    },
    replace: Boolean,
    state: {
        type: Object as PropType<State>,
    },
} as const

A <Navigate> element changes the current location when it is rendered. It's a component wrapper around useNavigate, and accepts all the same arguments as props.

import { Navigate } from '@bv3/router'

const LoginForm = defineComponent({
    setup() {
        const state = reactive({ user: null, error: null })

        async handleSubmit(event) {
            event.preventDefault()
            try {
                let user = await login(event.target)
                state.user = user
            } catch (error) {
                state.error = error
            }
        }

        return () => {
            let { user, error } = this.state
            return (
                <div>
                    {error && <p>{error.message}</p>}
                    {user && <Navigate to="/dashboard" replace={true} />}
                    <form onSubmit={handleSubmit}>
                        <input type="text" name="username" />
                        <input type="password" name="password" />
                    </form>
                </div>
            )
        }
    }
})

<Outlet>

Props declaration
const OutletProps = {} as const

An <Outlet> should be used in parent route elements to render their child route elements. This allows nested UI to show up when child routes are rendered. If the parent route matched exactly, it will render nothing.

function Dashboard() {
    return (
        <div>
            <h1>Dashboard</h1>

            {/* This element will render either <DashboardMessages> when the URL is
                "/messages", <DashboardTasks> at "/tasks", or null if it is "/"
            */}
            <Outlet />
        </div>
    )
}

function App() {
    return (
        <Routes>
            <Route path="/" element={<Dashboard />}>
                <Route path="messages" element={<DashboardMessages />} />
                <Route path="tasks" element={<DashboardTasks />} />
            </Route>
        </Routes>
    )
}

<Prompt>

Prompt declaration
const PromptProps = {
    message: {
        type: String,
        required: true,
    },
    when: Boolean,
} as const

A <Prompt> is the declarative version of usePrompt. It doesn't render anything. It just calls usePrompt with its props.

<Router>

Props declaration
const RouterProps = {
    action: {
        type: String as PropType<Action>,
        default: Action.Pop,
    },
    location: {
        type: Object as PropType<Location>,
    },
    navigator: {
        type: Object as PropType<Navigator>,
        required: true,
    },
    static: {
        type: Boolean,
        default: false,
    },
} as const

<Router> is the low-level interface that is shared by all router components (<BrowserRouter>, <HashRouter>, <StaticRouter>. In terms of Vue3, <Router> is a context provider that supplies routing information to the rest of the app.

You probably never need to render a <Router> manually. Instead, you should use one of the higher-level routers depending on your environment. You only ever need one router in a given app.

<Routes> and <Route>

Props declaration
const RoutesProps = {
    basename: {
        type: String,
        default: '',
    },
} as const

const RouteProps = {
    element: {
        type: Object as PropType<VNode | JSX.Element | (() => VNodeChild)>,
    },
    path: String,
    keepalive: Boolean,
} as const

<Routes> and <Route> are the primary ways to render something in BestVue3 Router based on the current location. You can think about a <Route> kind of like an if statement; if its path matches the current URL, it renders its element prop or slot! The <Route caseSensitive> prop determines if the matching should be done in a case-sensitive manner (defaults to false).

Whenever the location changes, <Routes> looks through all its children <Route> elements to find the best match and renders that branch of the UI. <Route> elements may be nested to indicate nested UI, which also correspond to nested URL paths. Parent routes render their child routes by rendering an <Outlet>.

<Routes>
    <Route path="/" element={<Dashboard />}>
        <Route path="messages" element={<DashboardMessages />} />
        <Route path="tasks" element={<DashboardTasks />} />
    </Route>
    <Route path="about" element={<AboutPage />} />
</Routes>

[!Note:]

If you'd prefer to define your routes as regular JavaScript objects instead of using JSX, try useRoutes instead.

The default <Route element> is an <Outlet>. This means the route will still render its children even without an explicit element prop, so you can nest route paths without nesting UI around the child route elements.

For example, in the following config the parent route renders an <Outlet> by default, so the child route will render without any surrounding UI. But the child route's path is /users/:id because it still builds on its parent.

<Route path="users">
    <Route path=":id" element={<UserProfile />} />
</Route>

You can pass keepalive to <Route> to control if this route should be kept when redirect away. KeepAlive is a very useful feature in Vue when people want to keep the state of a component and it's subtree when it unmounted. We implement it by render all routes under <KeepAlive>. And we guess it's a much clear way how we control which route should be kept.

<Routes>
    <Route path="stay" element={<Kept />} keepalive />
    <Route path="leave" element={<Lost />}>
</Route>

[!Note:]

The example before use JSX as syntax, in JSX pass VNode is really easy But we guess lots of developer still prefer SFC in a long time So we provide slot way to pass element

<template>
    <Routes>
        <Route path="/">
            <template v-slot:element>
                <Dashboard />
            </template>
            <Route path="messages">
                <template v-slot:element>
                    <DashboardMessages />}
                </template>
            </Route>
            <Route path="tasks" element={<DashboardTasks />}>
                <template v-slot:element>
                    <DashboardTasks />}
                </template>
            </Route>
        </Route>
    </Routes>
</template>

<StaticRouter>

Props declaration
const StaticRouterProps = {
    location: {
        type: [String, Object] as PropType<string | Record<string, any>>,
        default: '/',
    },
} as const

<StaticRouter> is used to render a BestVue3 Router web app in node. Provide the current location via the location prop.

  • <StaticRouter location> defaults to "/"
import { renderToString } from '@vue/server-renderer'
import { StaticRouter } from '@bv3/router'
import http from 'http'

async function requestHandler(req, res) {
    let html = await renderToString(
        <StaticRouter location={req.url}>
            {/* The rest of your app goes here */}
        </StaticRouter>,
    )

    res.write(html)
    res.end()
}

http.createServer(requestHandler).listen(3000)

createRoutesFromArray

Type declaration
declare function createRoutesFromArray(
    array: PartialRouteObject[],
): RouteObject[]

interface PartialRouteObject {
    path?: string
    caseSensitive?: boolean
    node?: VNode
    children?: PartialRouteObject[]
}

interface RouteObject {
    caseSensitive: boolean
    children?: RouteObject[]
    node: VNode
    path: string
}

createRoutesFromArray is a helper that fills in the (potentially) missing pieces in an array of route objects. It is used internally by useRoutes to create route objects.

createRoutesFromChildren

Type declaration
declare function createRoutesFromChildren(children: VNodeChild): RouteObject[]

createRoutesFromChildren is a helper that creates route objects from VNodes. It is used internally in a <Routes> element to generate a route config from its <Route> children elements.

See also createRoutesFromArray.

generatePath

Type declaration
declare function generatePath(path: string, params: Params = {}): string

generatePath interpolates a set of params into a route path string with :id and * placeholders. This can be useful when you want to eliminate placeholders from a route path so it matches statically instead of using a dynamic parameter.

generatePath('/users/:id', { id: 42 }) // "/users/42"
generatePath('/files/:type/*', { type: 'img', '*': 'cat.jpg' }) // "/files/img/cat.jpg"

Location

The term "location" in BestVue3 Router refers to the Location interface from the history library.

[!Note:]

The history package is BestVue3 Router's main dependency and many of the core types in BestVue3 Router come directly from that library including Location, To, Path, State, and others. You can read more about the history library in its documentation.

matchRoutes

Type declaration
declare function matchRoutes(
    routes: RouteObject[],
    location: string | PartialLocation,
    basename: string = '',
): RouteMatch[] | null

interface RouteMatch {
    route: RouteObject
    pathname: string
    params: Params
}

matchRoutes runs the route matching algorithm for a set of routes against a given location to see which routes (if any) match. If it finds a match, an array of RouteMatch objects is returned, one for each route that matched.

This is the heart of BestVue3 Router's matching algorithm. It is used internally by useRoutes and the <Routes> component to determine which routes match the current location. It can also be useful in some situations where you want to manually match a set of routes.

matchPath

Type declaration
declare function matchPath(
    pattern: PathPattern,
    pathname: string,
): PathMatch | null

type PathPattern =
    | string
    | { path: string; caseSensitive?: boolean; end?: boolean }

interface PathMatch {
    path: string
    pathname: string
    params: Params
}

matchPath matches a route path pattern against a URL pathname and returns information about the match. This is useful whenever you need to manually run the router's matching algorithm to determine if a route path matches or not. It returns null if the pattern does not match the given pathname.

The useMatch hook uses this function internally to match a route path relative to the current location.

resolvePath

Type declaration
declare function resolvePath(to: To, fromPathname = '/'): Path

type To = string | PartialLocation

interface Path {
    pathname: string
    search: string
    hash: string
}

resolvePath resolves a given To value into an actual Path object with an absolute pathname. This is useful whenever you need to know the exact path for a relative To value. For example, the <Link> component uses this function to know the actual URL it points to.

The useResolvedPath hook uses resolvePath internally to resolve against the current location.pathname.

useBlocker

Type declaration
declare function useBlocker(blocker: Blocker, getWhen: () => boolean): void

useBlocker is a low-level hook that allows you to block navigation away from the current page, i.e. prevent the current location from changing. This is probably something you don't ever want to do unless you also display a confirmation dialog to the user to help them understand why their navigation attempt was blocked. In these cases, you probably want to use usePrompt or <Prompt> instead.

useHref

Type declaration
declare function useHref(toEffect: () => To): string

The useHref hook returns a URL that may be used to link to the given to location, even outside of BestVue3 Router.

[!Tip:]

You may be interested in taking a look at the source for the <Link> component in @bv3/router to see how it uses useHref internally to determine its own href value.

useLocation

Type declaration
declare function useLocation(): Location

This hook returns the current location object. This can be useful if you'd like to perform some side effect whenever the current location changes.

import { watchEffect } from 'vue'
import { useLocation } from '@bv3/router';

function App() {
  let location = useLocation();

  watchEffect(() => {
    ga('send', 'pageview');
  });

  return () => // ...
}

useMatch

Type declaration
declare function useMatch(pattern: PathPattern): PathMatch | null

Returns match data about a route at the given path relative to the current location.

See matchPath for more information.

useNavigate

Type declaration
declare function useNavigate(): NavigateFunction

interface NavigateFunction {
    (to: To, options?: { replace?: boolean; state?: State }): void
    (delta: number): void
}

The useNavigate hook returns a function that lets you navigate programmatically, for example after a form is submitted.

import { defineComponent } from 'vue'
import { useNavigate } from '@bv3/router'

const SignupForm = defineComponent({
    setup() {
        const navigate = useNavigate()

        async function handleSubmit(event) {
            event.preventDefault()
            await submitForm(event.target)
            navigate('../success', { replace: true })
        }

        return () => <form onSubmit={handleSubmit}>{/* ... */}</form>
    },
})

The navigate function has two signatures:

  • Either pass a To value (same type as <Link to>) with an optional second { replace, state } arg or
  • Pass the delta you want to go in the history stack. For example, navigate(-1) is equivalent to hitting the back button.

useOutlet

Type declaration
declare function useOutlet(): Ref<VNode | null>

Returns the element for the child route at this level of the route hierarchy. This hook is used internally by <Outlet> to render child routes.

useParams

Type declaration
declare function useParams(): Ref<Params>

The useParams hook returns an object of key/value pairs of the dynamic params from the current URL that were matched by the <Route path>. Child routes inherit all params from their parent routes.

import { defineComponent } from 'vue'
import { Routes, Route, useParams } from '@bv3/router';

const ProfilePage = defineComponent({
    setup() {
        // Get the userId param from the URL.
        const paramsRef= useParams();

        return () => {
            const { userId } = paramsRef.value
            // ...
        }
    }
})

function App() {
  return (
    <Routes>
      <Route path="users">
        <Route path=":userId" element={<ProfilePage />} />
        <Route path="me" element={...} />
      </Route>
    </Routes>
  );
}

usePrompt

Type declaration
declare function usePrompt(
    messageEffect: () => string,
    whenEffect: () => boolean,
): void

The usePrompt hook may be used to confirm navigation before the user navigates away from the current page. This is useful when someone has entered unsaved data into a form, and you'd like to prompt them before they accidentally leave or close the tab and lose their work.

import { defineComponent, reactive } from 'vue'
import { usePrompt } from '@bv3/router'

const SignupForm = defineComponent({
    setup() {
        const state = reactive({})
        const messageRef = ref('Are you sure you want to leave?')
        usePrompt(() => messageRef.value, () => state.formData !== null)
        return () => // ...
    }
})

usePrompt uses window.confirm on the web.

[!Note:]

If you need a more custom dialog box, you will have to use useBlocker directly and handle accessibility issues yourself.

useResolvedPath

Type declaration
declare function useResolvedPath(toEffect: () => To): Path

This hook resolves the pathname of the location in the given to value against the pathname of the current location.

This is useful when building links from relative values. For example, check out the source to <NavLink> which calls useResolvedPath internally to resolve the full pathname of the page being linked to.

See resolvePath for more information.

useRoutes

Type declaration
declare function useRoutes(
    routesEffect: () => PartialRouteObject[],
    basenameEffect = () => string,
): () => VNode | null

The useRoutes hook is the functional equivalent of <Routes>, but it uses JavaScript objects instead of <Route> elements to define your routes. These objects have the same properties as normal <Route> elements, but they don't require JSX.

The return value of useRoutes is a function which render the result of matched routes. You can directly invoke the function in your render function.

import { defineComponent } from 'vue'
import { useRoutes } from '@bv3/router'

const App = defineComponent({
    setup() {
        const renderElement = useRoutes([
            {
                path: '/',
                element: <Dashboard />,
                children: [
                    { path: 'messages', element: <DashboardMessages /> },
                    { path: 'tasks', element: <DashboardTasks /> },
                ],
            },
            { path: 'team', element: <AboutPage /> },
        ])

        return () => renderElement()
    },
})

See also createRoutesFromArray.

useSearchParams

Type declaration
declare function useSearchParams(
    defaultInitEffect: () => URLSearchParamsInit,
): [
    Ref<URLSearchParams>,
    (
        nextInit: URLSearchParamsInit,
        navigateOptions?: { replace?: boolean; state?: State },
    ) => void,
]

type ParamKeyValuePair = [string, string]
type URLSearchParamsInit =
    | string
    | ParamKeyValuePair[]
    | Record<string, string | string[]>
    | URLSearchParams

The useSearchParams hook is used to read and modify the query string in the URL for the current location. useSearchParams returns an array of two values: the Ref of current location's search params and a function that may be used to update them.

import { defineComponent } from 'vue'
import { useSearchParams } from '@bv3/router'

const App = defineComponent({
    setup() {
        let [searchParamsRef, setSearchParams] = useSearchParams()

        function handleSubmit(event) {
            event.preventDefault()
            // The serialize function here would be responsible for
            // creating an object of { key: value } pairs from the
            // fields in the form that make up the query.
            let params = serializeFormQuery(event.target)
            setSearchParams(params)
        }

        return () => (
            <div>
                <form onSubmit={handleSubmit}>{/* ... */}</form>
            </div>
        )
    },
})

[!Note:]

The setSearchParams function works like navigate, but only for the search portion of the URL. Also note that the second arg to setSearchParams is the same type as the second arg to navigate.

A createSearchParams(init: URLSearchParamsInit) function is also exported that is essentially a thin wrapper around new URLSearchParams(init) that adds support for using objects with array values. This is the same function that useSearchParams uses internally for creating URLSearchParams objects from URLSearchParamsInit values.

defineRouteComponent

This is a help funtion for typescript, in BestVue3 Router you can use any Component as Route, for example:

function App() {
    return (
        <Routes>
            <Home path="/">
            <About path="/about">
        </Routes>
    )
}

It work exactly the same as <Route path="/" element={<Home />}> in javascript. But you obviously won't define the path prop in your compoent, in typescript this will emit type check error.

The defineRouteComponent have the some type declaration with defineComponent in Vue3 except the return component will auto contain path prop declaration.