005 优雅的在 umi 页面间穿梭

3,917 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

这节课我们主要学习如何通过声明式和命令式的方式实现客户端页面间的导航。并运用 React Route 6 的 API 获取路由上的信息。

上一节课之后,我们已经有了两个页面,首页和用户页面,这节课我们将在首页使用声明式方式跳转到用户页面,然后在用户页面使用命令式跳转到首页。

声明式

在网站的页面之间链接时,你通常使用 <a> HTML 标签。

在 Umi 中,你使用了从 umi 导出的 <Link> 组件对应用程序中的不同页面进行客户端导航。

首先,在 pages/index.js 中,从 umi 导入 Link 组件,方法是在顶部添加这一行:

import { Link } from 'umi';

然后修改 div 标记中的这一行:

Index Page

修改为:

import { Link } from 'umi';

export default () => <div>Index Page
    <p><Link to="/user">Go to user page</Link></p>
</div>;

命令式(history、useNavigate)

上述内容,我们在首页使用声明式的方法添加了一个跳转到 user 页面的方法,接下来我们通过命令式的方式,从 user 返回首页。

接下来,将 pages/user/index.js 的内容改为:

import { history, useNavigate } from 'umi';

export default function User() {
    const navigate = useNavigate();
    return (
        <div>
            <h1>User Page</h1>
            <button onClick={() => history.back()}>go back by history!</button>
            <button onClick={() => history.push('/')}>go to index by history!</button>
            <button onClick={() => navigate(-1)}>go back by navigate!</button>
            <button onClick={() => navigate('/')}>go to index by navigate!</button>
        </div>
    );
}

上面的页面中,我们增加了四个按钮,使用 historyuseNavigate 分别实现了回退方法,和页面跳转方法到达首页。

虽然从效果是来看都是从列表页跳转到了主页,但需要注意的是,使用 back 方法,会撤回一次浏览器历史。也就是说,你无法使用浏览器上面的后退按钮(包括安卓设备上的返回键),返回 user 页面。而使用 push 返回,会增加一个浏览器历史。如果多次在两个页面之间 push ,会导致使用回退按钮返回页面时,会有一个很长的浏览器历史列表。

back 方法完全依赖于项目的浏览历史,也就是说完全依赖于你的“前一个页面”。这意味着,当你在当前路由刷新页面时, back 有可能会失效。所以实际项目中,要根据具体的项目逻辑和场景来选择合适的方法。

虽然命令式 historyuseNavigate 看起来都可以实现在页面之间导航,但是需要注意的是 useNavigate 只能用在 React 存在的上下文中,简单的说就是他只能在组件的生命周期中使用,如果你在全局的文件,比如 app.ts 或者 global.ts 文件中就不能使用 useNavigate ,只能使用 history

使用 url 传参(SearchParams)

url 传参就是 url 中的 search 对象,来进行页面之间的参数传递,是一种特别常用的前端传参手段,就是我们经常看到 url 中带有一个 ? 后面跟着一些 xx=yy&&dd=mm 之类的字符。 我们可以在页面中取出它们来进行页面逻辑编写。

首先我们介绍一下 Umi 中如何取到 url 中的参数。

import { useSearchParams } from 'umi';

export default () => {
    const [searchParams, setSearchParams] = useSearchParams();
    const a = searchParams.get('a');
    const b = searchParams.get('b');

    return <div>
        <p>SearchParams ---- a:{a};b:{b}</p>
    </div>
};

我们使用 useSearchParams 来取到 url 中携带的参数,用法非常的便捷。 有读数据,当然也有写数据操作。

刷新 SearchParams

可以分为两种情况,当前页面可以使用 useSearchParams 返回的第二参数,刷新 SearchParams

import { Link, useSearchParams, createSearchParams } from 'umi';

export default () => {
    const [searchParams, setSearchParams] = useSearchParams();
    const a = searchParams.get('a');
    const b = searchParams.get('b');
    return <div>
        <p>SearchParams ---- a:{a};b:{b}</p>
        <button onClick={() => {
            setSearchParams(createSearchParams({ a: 123, b: 456 }));
        }}>Change SearchParams</button>
    </div>
};

写入 SearchParams

第二种情况是从别的页面跳转的时候携带 SearchParams

import { useNavigate, createSearchParams } from 'umi';

export default function User() {
    const navigate = useNavigate();
    return (
        <div>
            <button onClick={() => {
                navigate(`/?${createSearchParams({ a: 1, b: 2 })}`)
            }}>go to index has SearchParams!</button>
        </div>
    );
}

操作 SearchParams 我们都可以通过 Umi 提供的 createSearchParams API 来很轻松的完成。

使用 SearchParams 最大的好处就是数据永久化,只要你将完整的链接发送给用户,那链接中都将会携带这些参数。坏处就是,参数都是显示存在的,对于一些安全性场景的敏感数据,可能太不友好。所以我们可以使用另外一种隐蔽性更强的方式实现参数传递。

Route State

import { useNavigate } from 'umi';

export default function User() {
    const navigate = useNavigate();
    return (
        <div>
            <button onClick={() => {
                navigate('/', {
                    state: {
                        c: 987
                    }
                })
            }}>go to index has State!</button>
        </div>
    );
}

在首页可以在 location 对象中取到 State,这个和 React 中的 State 类似,只不过它是一个临时性的数据,当你在全新的环境打开这个链接,将会丢失这个数据。

可以使用 useLocation 获取到 location

import { useLocation } from 'umi';

export default () => {
    const location = useLocation();
    return <div>
        <p>State ---- {location.state}</p>
    </div>
};

两种参数传递的方式,都有自己的利弊,分别应对不用的项目交付场景,在真实的项目中可以充分分析需求,权衡利弊来使用不同的方式。

当然以上提到的两种方式,是纯路由传参的手段,我们也可以通过前端数据流的方式来实现参数传递。当需要传递一个比较大的对象时,还可以借助服务端的能力,将数据保存到服务端,到新页面再通过接口获取。

源码归档