问题
这几年忙着写 Taro 相关业务,所以很久没有再接触 ReactRouter 了。从当年使用的 ReactRouter v3 & VueRouter v2,功能和写法都没什么差别,而到现在的 ReactRouter v6,就感觉变化十分大。这里从使用者的角度聊聊,初次上手 v6 的感受和如何应对这些变化。
变化
范式
函数化和标准化,让其源码减少了一半
- v6 全面拥抱 Hooks,API 不再集中在一个对象上。同时这意味着如果你的项目还不支持 Hooks,那就应该使用更早前的版本。
- 使用 URLSearchParams。它是标准化的 WebAPI,
react-router-dom
库目标是尽可能往标准化靠拢,少一些自定义对象和方法。
URL 参数获取
- useParams() 用于获取路径参数,比如 URL 格式是 '/user-info/:userId',就可以用
let { userId } = useParams()
获得用户 id。 useSearchParams() 用于获取 search 参数,比如常见的 '?name=foo&id=boo' ,就可以用
[searchParams, setSearchParams] = useSearchParams()
得到数据和数据修改方法- 从教程和 searchParams 的类型可知,searchParams 的类型是 URLSearchParams,不是普通对象。
- 参数值要通过
searchParams.get('name')
的方式获取。 - 通过 setSearchParams() 可以即时改变当前 URL 的 search 部分
URL 参数设置
官方文档的例子,只提供了手动拼接路径参数或者 search 参数成 URL 的方式,比如
const navigate = useNavigate();
const url = 'foo/boo' + '?name=1&id=2';
navigate(url);
但是,我们希望在编写业务逻辑的时候可以方便地传入对象,比如 navigate('foo/boo', {name:1, id:2})
。再根据上面提到的,范式上倾向于使用 URLSearchParams,所以我们封装了以下方法
/**
* @description: 路由参数设置到 url 上,得到新的 url
* @param url 跳转地址
* @param params 路由参数(非必填,所有单据都是一个路由,只有参数不一样,这个时候需要增加这个参数)
* @return 组合出来的带参数地址
*/
function setRouterParams(url: string, params?: Record<PropertyKey, any>): string {
const searchParams = new URLSearchParams(params);
return `${url}?${searchParams.toString()}`;
}
const url = setRouterParams('foo/boo', {name:1, id:2}); // 'foo/boo?name=1&id=2'
navigate(url);
History 对象
我们想要监听 history 的变化,以动态改变页面的标题。但是搜索发现,createBrowserHistory()
已经不在文档内了。
从 issue 讨论可知,v6.4 后 history 的主要功能已经合并到数据路由中(通过 createHashRouter(), createBrowserRouter()
之类生成的路由),比如想要实现监听功能
const router = createHashRouter(routerList);
function App() {
const [pageTitle, setPageTitle] = useState('');
const { hash } = window.location;
useEffect(() => {
router.subscribe(({ location }) => {
const { pathname } = location;
const targetRoute = routerList.find((item) => item.path === pathname);
setPageTitle(targetRoute?.title || ''); // 标题跟着变化
});
}, []);
return (
<div className='App'>
{/* react-helmet 可以方便地修改 document.title */}
<Helmet>
<title>{pageTitle}</title>
</Helmet>
<RouterProvider router={router} />
</div>
);
}
更多特性变化未完待续...
总结
React 的相关插件都有函数化和标准化的趋势。为了更好地适应技术更新,我们需要更新 WebAPI 知识,更熟练地掌握 Hooks 范式。
不过还是需要吐槽,Hooks 的初衷是,渲染是一种功能而已,没必要把属性都绑定在 state 里,所以只需要把渲染作为函数引入即可。它的目的是不是“反 OOP”,只是 Hooks 范式更好。ReactRouter v6 把功能都拆散,业务逻辑的模块化程度就降低了,无疑增加了学习和维护成本。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。