拥抱开源,发挥更多Vue3的可能性,给自己多一个选择

·  阅读 853

为什么另外一个路由库

Vue 社区已经有一个官方的路由库了,为什么需要另外一个?

怎么说呢,对于 API 的喜好每个人都会不一样吧。我是一个比较追求极致体验的人,在之前使用 React 进行开发的时候,我就是一个痴迷于一切皆组件的理念的人,我曾经丧心病狂地尝试封装<Axios>组件,通过传递 props 来获取数据,没有尝试过的同学可能不能领会这种近乎于偏执的追求。其实在 React 生态这种尝试非常常见,你任何能想象到的前端技术大概都能找到组件。

所以在后来用 Vue 的时候,就非常不习惯各种:

  • mixins
  • 原型链上挂方法(说实话这是非常差的开发方式)
  • SFC

但是 Vue3 的 composition API 让我看到了希望,曾经几乎只能通过 mixin 来扩展的功能,现在有了更好的解决方案。而且我也终于可以摆脱this了(我真的非常讨厌this)。我从 19 年接触 Vue3 开始就在研究 JSX 开发方式,在 Vue3 中一切皆组件的目的并不比 React 难,但是社区并没有很多人追求这个方向,那么,我就自己做吧。

所以,就有了 BestVue3 Router 这个库,看完介绍相信你们就能体会到一些这种感受吧。

视频教程

更多:

Star、Issue和PR都是对开源最好的支持!

介绍

BestVue3 Router 的核心是route的概念。路由代表着一个你应用中的"页面" 。BestVue3 Router 代表着内部包含 URLs 的路由器,也叫做"locations"。BestVue3 Router 让你定义用户访问路由渲染的 VNodes。

一个简单的有两个页面的网页应用,"home"和"about"可能看起来类似这样:

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')
复制代码

<Router> element提供当前[location]../api-reference.md#location)的信息给剩余的后代。这个例子使用<BrowserRouter>。你应该只渲染一个唯一的<Router>在你的根组件附近。

<Routes> element是你用来定义有哪些路由以及当路由匹配当前路径时这个<Route> element应该渲染什么内容。

在这篇教程的接下去的例子中我们预设你已经引入来 Vue3 并且在<Router>内渲染<App>节点,所以我们简明地只展示需要的<Routes>内容。

导航

BestVue3 Router 提供了一个 Link 组件你可以用来让你的用户在不同的页面之间[导航]../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>
    )
}
复制代码

读取路径参数

你可以在你的<Route path> 使用动态的:id-类似部分来提取值用来请求数据或者渲染一些内容。useParams hook返回一个Ref对象,其value是一个包含路径参数的对象。

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>
    )
}
复制代码

模糊路径和打分

当确定哪个路由来渲染的时候,Routes节点选择和当前位置最匹配的路径,通常是哪些更明确的路径。

比如,path="invoices/sent"只会匹配/invoices/sent,所以他比path="invoices/:invoiceId"在匹配以/invoices (/invoices/123, /invoices/cupcakes, 等)开头的 URL 时更加明确。你可以根据你的喜好按照任意顺序组织你的代码。

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>
    )
}
复制代码

嵌套路由

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.

路由可以是嵌套的,他们的路径也会是嵌套的。高层级的路由渲染的组件需要渲染一个 <Outlet> 节点来让他们的子路由可以被渲染。

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

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

            {/*
                这个节点渲染他的子路由,在这个例子中可能是 <SentInvoices> 或者 <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>
    )
}
复制代码

注意在上面这个例子中路由是如何嵌套在父路由中的。这个嵌套的行为在创建导航和容器不变子内容根据路由变化的布局的时候非常有用。

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>
    )
}
复制代码

相对的链接

相对的<Link to>值(不以/开始) 是相对于渲染他们的路由的路径的。下面的两个链接指向/dashboard/invoices/dashboard/team因为他们都是在<Dashboard>下渲染的。这在你修改父路径或者重新安排你的组件结构的时候非常好用因为所有你的链接自动会更新。

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>
    )
}
复制代码

主路由

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.

嵌套路由可以使用path="/"来表明他们应该在他的父路由的路径下渲染。你可以认为这些路由就像其他子路由的主页。

function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} />
            <Route path="dashboard" element={<Dashboard />}>
                <Route path="/" element={<DashboardHome />} />
                <Route path="invoices" element={<DashboardInvoices />} />
            </Route>
        </Routes>
    )
}
复制代码

"Not Found" 路由

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.

当没有其他路由匹配的时候,你可以使用path="*"渲染一个"not found"路由。这个路由会匹配任何 URL,但也会具有最弱的优先级,所以路由器只会在找不到其他匹配的路由的情况下选择他。

function App() {
    return (
        <Routes>
            <Route path="/" element={<Home />} />
            <Route path="dashboard" element={<Dashboard />} />
            <Route path="*" element={<NotFound />} />
        </Routes>
    )
}
复制代码

多组路由

虽然在你的应用中应该只有一个<Router>,你可以根据需要有多个<Routes>。每个<Routes>独立地选择子路由进行渲染。

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>
    )
}
复制代码

后辈路由集合

你可以在任何你需要的地方渲染<Routes> 节点,包括在其他<Routes>的子树中。除了他们会自动在渲染他们的路由的基础上构建路径之外,他们跟其他<Routes>一样正常工作。如果你需要这么做,请确保在父路由的路径最后放上*。不然的话父路由不会匹配比他路径长的 URL,你的后辈<Routes>永远不会展示。

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>
    )
}
复制代码

编程式的导航

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.

如果你需要编程式地导航(比如在用户提交表单之后),使用useNavigate 钩子来获取一个函数帮你进行导航。

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>
        )
    },
})
复制代码

以上!这里我们还没有覆盖所有的 API,但是这些绝对是最通用的场景。如果你想要再深入学习,你可以查看完整的 API 文档

对于我们正在做的事情感兴趣,或者希望能够学到更多更优质的Vue3知识,可以搜索公众号 BestVue3 关注,提供更优质的Vue3内容。

分类:
前端
标签: