Why another routing library

The Vue community already has one official routing library, why need another?

What can I say? Everyone has different preferences for apis. I am a person who is in pursuit of extreme experience. When DEVELOPING React, I was obsessed with the concept of all components. I tried desperately to encapsulate

components and obtain data by passing props. In fact, this kind of experiment is so common in the React ecosystem that you can probably find components for any front-end technology you can imagine.

Therefore, when I used Vue later, I was not used to all kinds of:

  • mixins
  • The prototype chain approach (which is very bad development to be honest)
  • SFC

But Vue3’s Composition API gives me hope that what was once almost exclusively extended by mixins now has a better solution. And I can finally get rid of this (I really hate this). I have been studying JSX development since 19 years of Vue3. The purpose of everything component in Vue3 is not as difficult as React, but there are not many people in the community pursuing this direction, so I will do it myself.

So, there is BestVue3 Router library, after reading the introduction, I’m sure you can get some sense of this feeling.

Video tutorial

More:

  • Why use JSX to develop Vue3 applications
  • The project address
  • Full API documentation
  • A set of Demo

Star, Issue and PR are the best support for open source!

introduce

At the heart of BestVue3 Router is the concept of a route. A route represents a “page” in your application. BestVue3 Router represents a Router that contains URLs internally, also called “locations”. BestVue3 Router lets you define VNodes for user access to route rendering.

A simple two-page web application, “home” and “About”, might look something like this:

import { createApp } from 'vue'
import { BrowserRouter as Router, Routes, Route } from '@bv3/router'

function App() {
    return (
        <div>
            <h1>Welcome</h1>
            <Routes>
                <Route path="/" element={<Home />} / ><Route path="about" element={<About />} / ></Routes>
        </div>
    )
}

createApp(() = > (
    <Router>
        <App />
    </Router>
)).mount('#aa')
Copy the code


Element provides the current [location].. /api-reference.md#location) for the rest of the descendants. This example uses

. You should render only a unique

near your root component.



Element is what you use to define which Routes are available and what the

element should render if the Route matches the current path.

In the following examples in this tutorial we assume that you have already introduced Vue3 and rendered the

node within the

, so we simply show the

content that we need.


navigation

BestVue3 Router provides a Link component that you can use to let your users [navigate] between different pages.. /api-reference.md#navigation)

import { Routes, Route, Link } from '@bv3/router'

function Home() {
    return (
        <div>
            <h1>Home</h1>
            <nav>
                <Link to="/">Home</Link> | <Link to="about">About</Link>
            </nav>
        </div>)}function About() {
    return <h1>About</h1>
}

function App() {
    return (
        <div>
            <h1>Welcome</h1>
            <Routes>
                <Route path="/" element={<Home />} / ><Route path="about" element={<About />} / ></Routes>
        </div>)}Copy the code

Read path parameter

You can use dynamic: ID – like sections in your

to extract values to request data or render something. UseParams hook returns a Ref object whose value is an object containing path parameters.

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

const Invoice = defineComponent({
    setup() {
        const paramsRef = useParams()
        return () = > <h1>Invoice {paramsRef.value.invoiceId}</h1>}})function App() {
    return (
        <Routes>
            <Route path="invoices/:invoiceId" element={<Invoice />} / ></Routes>)}Copy the code

Fuzzy paths and scoring

When determining which path to render, the Routes node selects the path that best matches the current location, which is usually more explicit.

For example, path=”invoices/sent” will only match /invoices/sent. Therefore, it is more precise when matching urls starting with /invoices (/invoices/123, /invoices/cupcakes, etc.) than when path=”invoices/:invoiceId”. You can organize your code in any order you like.

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

function Invoice() {
    const { invoiceId } = useParams()
    return () = > <h1>Invoice {invoiceId}</h1>
}

function SentInvoices() {
    return <h1>Sent Invoices</h1>
}

function App() {
    return (
        <Routes>
            <Route path="invoices/:invoiceId" element={<Invoice />} / ><Route path="invoices/sent" element={<SentInvoices />} / ></Routes>)}Copy the code

Embedded routines by

Routes may be nested inside one another, and their paths will nest too. Components that are used higher in the route hierarchy may render an <Outlet> element to render their child routes.

Routes can be nested, and their paths will also be nested. Higher-level route rendering components need to render an

node to make their child routes renderable.

import { Routes, Route, Outlet } from '@bv3/router'

function Invoices() {
    return(<div> <h1>Invoices</h1> {/* This node renders its Invoices. In this case it might be <SentInvoices> or <IndividualInvoice> */} <Outlet /> </div>)} const IndividualInvoice = defineComponent({} setup() { const paramseRef = useParams() return () => <h1>Invoice {paramseRef.value.invoiceId}</h1> }, }) function SentInvoices() { return <h1>Sent Invoices</h1> } function App() { return ( <Routes> <Route path="invoices" element={<Invoices />}> <Route path=":invoiceId" element={<IndividualInvoice />} /> <Route path="sent" element={<SentInvoices />} /> </Route> </Routes> ) }Copy the code

Notice how the route is nested within the parent route in the example above. This nested behavior is useful when creating navigation and container layouts that do not change the child content based on the route.

import { Routes, Route, Link, Outlet } from '@bv3/router'

function Layout() {
    return (
        <div>
            <h1>Welcome to the app!</h1>
            <nav>
                <Link to="invoices">Invoices</Link>| {'}<Link to="dashboard">Dashboard</Link>
            </nav>
            <div className="content">
                <Outlet />
            </div>
        </div>)}function Invoices() {
    return <h1>Invoices</h1>
}

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

function App() {
    return (
        <Routes>
            <Route path="/" element={<Layout />} ><Route path="invoices" element={<Invoices />} / ><Route path="dashboard" element={<Dashboard />} / ></Route>
        </Routes>)}Copy the code

Relative link

The relative values (not starting with /) are relative to the path rendering their route. The following two links point to /dashboard/invoices and /dashboard/team because they are both rendered under < dashboard >. This is great when you modify parent paths or rearrange the structure of your components because all your links are automatically updated.

import { Routes, Route, Link, Outlet } from '@bv3/router'

function Home() {
    return <h1>Home</h1>
}

function Dashboard() {
    return (
        <div>
            <h1>Dashboard</h1>
            <nav>
                <Link to="invoices">Invoices</Link> <Link to="team">Team</Link>
            </nav>
            <hr />
            <Outlet />
        </div>)}function Invoices() {
    return <h1>Invoices</h1>
}

function Team() {
    return <h1>Team</h1>
}

function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} / ><Route path="dashboard" element={<Dashboard />} ><Route path="invoices" element={<Invoices />} / ><Route path="team" element={<Team />} / ></Route>
        </Routes>)}Copy the code

The main road by

Nested routes may use path="/" to indicate they should render at the path of the parent component. You can think about these routes like index pages for the rest of the child routes.

Nested routines can use path=”/” to indicate that they should be rendered under the path of their parent route. You can think of these routes as the home pages of other child routes.

function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} / ><Route path="dashboard" element={<Dashboard />} ><Route path="/" element={<DashboardHome />} / ><Route path="invoices" element={<DashboardInvoices />} / ></Route>
        </Routes>)}Copy the code

“Not Found” routing

When no other route matches the URL, you can render a “not found” route using path="*". This route will match any URL, but will have the weakest precedence so the router will only pick it if no other routes match.

When no other routes match, you can render a “not found” route with path=”*”. This route will match any URL, but will also have the weakest priority, so the router will only select it if no other matching route can be found.

function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} / ><Route path="dashboard" element={<Dashboard />} / ><Route path="*" element={<NotFound />} / ></Routes>)}Copy the code

Multiple sets of routing

Although there should be only one

in your application, you can have more

as needed. Each

independently selects child Routes to render.


function App() {
    return (
        <div>
            <Sidebar>
                <Routes>
                    <Route path="/" element={<MainNav />} / ><Route path="dashboard" element={<DashboardNav />} / ></Routes>
            </Sidebar>

            <MainContent>
                <Routes>
                    <Route path="/" element={<Home />} ><Route path="about" element={<About />} / ><Route path="support" element={<Support />} / ></Route>
                    <Route path="dashboard" element={<Dashboard />} ><Route path="invoices" element={<Invoices />} / ><Route path="team" element={<Team />} / ></Route>
                    <Route path="*" element={<NotFound />} / ></Routes>
            </MainContent>
        </div>)}Copy the code

Descendant route collection

You can render the

node wherever you want, including in other

subtrees. They work just like any other

except that they automatically build paths based on their rendered Routes. If you need to do this, be sure to put * at the end of the path of the parent route. Otherwise the parent route will not match a URL longer than its path and your descendant

will never be displayed.



function Dashboard() {
    return (
        <div>
            <p>Look, more routes!</p>
            <Routes>
                <Route path="/" element={<DashboardGraphs />} / ><Route path="invoices" element={<InvoiceList />} / ></Routes>
        </div>)}function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} / ><Route path="dashboard/*" element={<Dashboard />} / ></Routes>)}Copy the code

Programmatic navigation

If you need to navigate programmatically (like after the user submits a form), use the useNavigate hook to get a function you can use to navigate.

If you need to navigate programmatically (such as after the user submits a form), use the useNavigate hook to get a function to navigate for you.

import { useNavigate } from '@bv3/router'

const Invoices = defineComponent({
    setup() {
        const navigate = useNavigate()
        return () = > (
            <div>
                <NewInvoiceForm
                    onSubmit={async event= > {
                        const newInvoice = await createInvoice(event.target)
                        navigate(`/invoices/${newInvoice.id}`)
                    }}
                />
            </div>)}})Copy the code

The above! We haven’t covered all the apis here, but these are definitely the most common scenarios. If you want to learn more, you can check out the full API documentation.

If you are interested in what we are doing, or want to learn more and better Vue3 knowledge, you can search the public account BestVue3 to provide better Vue3 content.