4k字介绍 React Router 6.4 超大变化:引入 Data API。你不纯粹了!|世界观天下
背景
每次打开 React Router 官方文档,都会有惊吓,API又又又变了!这次看看有什么更新。
好家伙!这是我认知中的 React Router 吗?
我2022年3月开发《联机桌游合集》时,在用 6.2 版本,那时候 v6 跟 v5 v4 相比,API 已经发生了比较大的变化,但我认可这些变化。
(资料图片仅供参考)
现在看完 6.4 版本文档, 我想吐槽。 我的核心观点是:React Router 6.4 不再是纯粹的路由组件了,它耦合了数据获取逻辑。
下面本文 客观介绍: React Router 6.4 引入的新功能 Data API,并在最后给 主观结论。
1. 新增 createXXXXRouter
API
1.1 介绍
在 React Router 6.4 中,新增了 3 个 createXXXXRouter
API,用于支持 data API:
createBrowserRouter
createMemoryRouter
createHashRouter
也就是说,如果你不用这3个API,而是像v6.0
-v6.3
一样,直接使用
等下面几个API,那么你享受不到 data API。
1.2 createXXXXRouter
用法
必须结合
一起使用。可以看到,它使用一个配置,定义路由。
import * as React from "react";import * as ReactDOM from "react-dom";import { createBrowserRouter, RouterProvider,} from "react-router-dom";const router = createBrowserRouter([ { path: "/", element: , children: [ { path: "team", element: , }, ], },]);ReactDOM.createRoot(document.getElementById("root")).render( );
1.3 也可用JSX定义路由
当然,如果你喜欢用JSX语法定义路由,像
一样:
} />
React Router 6.4 也提供了JSX配置,参考createRoutesFromElements,它有另外一个名字叫createRoutesFromChildren。
const router = createBrowserRouter( createRoutesFromElements( }> } /> } /> ));
2.
的变化
2.1 什么是 Data API?
当你使用createXXXXRouter
和
时,你就可以使用 Data API。
说了这么多,什么是 Data API 呢?
其实就是允许你把「数据获取逻辑」写到路由定义中。每当路由切换到那里时,会自动获取数据。
我们从
的变化就可以看出,它新增了3个相关的属性:
2.2
loader 属性
loader属性传入一个函数(允许是 async function),每次渲染「该路由对应的element」前执行函数。在「该路由对应的element」内,可以使用 hookuseLoaderData
(下文会介绍)来获取这个函数的返回值(通常是http请求的response)。
{ // loaders can be async functions const res = await fetch("/api/user.json", { signal: request.signal, }); const user = await res.json(); return user; }} element={ }/>
2.2.1 loader 参数
loader属性传入的函数,允许有2个参数:
params: 如果Route中包含参数(例如path是/user/:userId
,参数就是:userId
,可以通过params.userId获取到路由参数的值)。request: 是 Web 规范中,Fetch API 的 Request,代表一个请求。注意:这里指的不是你在 loader 内部发的 fetch 请求,而是当用户路由到当前路径时,发出的“请求”(其实在Single-Page App中,router已经拦截了这个真实的请求,只有Multi-Page App中才会有这个请求),这里是 React Router 6.4 为了方便开发者获取当前路径信息提供的参数,他们按照 Web规范,制造了一个假的 request。你可以通过 request
方便的获取当前页面的参数: { const url = new URL(request.url); const searchTerm = url.searchParams.get("q"); return searchProducts(searchTerm); }}/>
不要这个 request 参数行吗?不行,因为如果你用window.location
获取的信息是当前最新的值,如果用户快速的点击按钮,让页面路由到A,并立马路由到B,这时候路由A对应的Route的loader获取window.location
时,就可能拿到错误的值。
注意,传递 request,还有个好处,它有个 request.signal,当用户快速的点击按钮,让页面路由到A,并立马路由到B,页面A的loader的请求应该被取消掉,可以通过 signal 实现,如下:
{ return fetch("/api/teams.json", { signal: request.signal, }); }}/>
2.2.2 loader 返回值
函数的返回值,将可以在element中通过hook useLoaderData
(下文会介绍)来获取。你返回什么,它就拿到什么。
但是 React Router 官方建议,返回一个 Web规范 中的 Fetch API 的 Response。
你可以直接 return fetch(url, config);
,也可以自己构造一个假的 Response:
function loader({ request, params }) { const data = { some: "thing" }; return new Response(JSON.stringify(data), { status: 200, headers: { "Content-Type": "application/json; utf-8", }, });}//...
也可以通过 React Router 提供的 json 来构造:
import { json } from "react-router-dom";function loader({ request, params }) { const data = { some: "thing" }; return json(data, { status: 200 });}//...
2.2.2.1 特殊返回值: redirect
在 loader 中,可能校验后需要重定向,React Router 不建议你用 useNavigation 完成,建议直接在 loader 中直接 return redirect,跳转到新的网址。
import { redirect } from "react-router-dom";const loader = async () => { const user = await getUser(); if (!user) { return redirect("/login"); }};
2.2.3 loader 内抛出异常
如果数据获取失败,或者其它任何原因,你认为不能让 Route 对应的 element 正常渲染了,你都可以在 loader 中 throw 异常。这时候,「errorElement」就会被渲染。
function loader({ request, params }) { const res = await fetch(`/api/properties/${params.id}`); if (res.status === 404) { throw new Response("Not Found", { status: 404 }); } return res.json();}//...
注意:你可以抛出任何异常,都可以在 errorElement 内通过 hook useRouteError
来获取到异常。
但是,React Router 官方建议你 throw Response:
} errorElement={ } loader={async ({ params }) => { const res = await fetch(`/api/properties/${params.id}`); if (res.status === 404) { throw new Response("Not Found", { status: 404 }); } const home = res.json(); return { home }; }}/>
你依然可以用 React Router 提供的 json 方法,方便的构造个 Response:
throw json( { message: "email is required" }, { status: 400 },);
2.3
element 属性
这个不是新属性,即
2.3.1 内部可用 useLoaderData
获取 loader 返回值
注意,如果 loader 返回值是 Response,并且 Response 的 Content Type 是 application/json,React Router 内部会自动调用 .json() 方法,开发者不必写 .json() 了。
function Albums() { const albums = useLoaderData(); return {albums};}const router = createBrowserRouter([ { path: "/", loader: fetch("/api"), element: , },]);ReactDOM.createRoot(el).render( );
2.3.2 内部可调用 useRouteLoaderData
获取 其它 Route 的 loader 返回值
React 组件可以嵌套,
也可以嵌套,这时可以通过该 hook 获取其它
的 loader 的返回值。当然,你需要提供 id。
定义路由时:
createBrowserRouter([ { path: "/", loader: () => fetchUser(), element: , id: "root", children: [ { path: "jobs/:jobId", loader: loadJob, element: , }, ], },]);
内部调用这个hook时:
const user = useRouteLoaderData("root");
2.4
errorElement 属性
当 loader 内抛出异常,
就不渲染它的 element 了,而是渲染它的 errorElement。
2.4.1 异常可以冒泡
是可以嵌套的,每一层都可以定义 errorElement,异常发生后,会找到最近的 errorElement,并渲染它,然后停止冒泡。
2.4.2 内部可用 useRouteError
获取异常
在 errorElement 内,可用 useRouteError
获取异常。
const error = useRouteError();
2.4.3 内部可用 isRouteErrorResponse
判断异常类型
React Router 给了一个函数 isRouteErrorResponse
,帮你在开发 errorElement 时,可以判断当前异常是否是 Response 异常。因为 Response 异常 通常是开发者自己抛出的,是可以展示原因的(包括后端接口返回错误码和错误提示文案,也可在这里处理)。其它异常,通常是未知的,就直接展示兜底的报错文案即可。
function RootBoundary() { const error = useRouteError(); if (isRouteErrorResponse(error)) { if (error.status === 404) { return This page doesn"t exist!; } if (error.status === 503) { return Looks like our API is down; } } return Something went wrong;}
2.5
action 属性
它很像 laoder,你看:
它也有2个参数:params 和 request。定义跟 loader 一样。你可以 return 任何东西,同样 React Router 建议你 return Response。你也可以 return redirect,实现重定向。在element内,你可以用hookuseActionData
获取 action 返回值。(类似 useLoaderData
)不同点在于,它们执行时机不同:
loader 是用户通过 GET 导航至某路由时,执行的。action 是用户提交 form 时,做 POST PUT DELETE 等操作时,执行的。以前写过的都知道,它有 action 和 method 参数,在以前,提交表单也是在浏览器内做了一次改变URL的操作。使用React后,几乎没人这么做,大家都是AJAX或Fetch提交表单了。
现在,React Router 提供了 组件,并给
组件增加了 action
属性,让提交表单也变成一次路由。
实在是忍不住了,想发表个人观点:感觉没用,屁用没有。
如果你想了解 Route 的 action 属性,一定要看 React Router Form,注意 Form 里也有个 action 属性,不要搞混了。
3. React Router 6.4 其它特性
当然,React Router 6.4 不仅有 Data API 这一个特性,它另一个重大更新是:Deferred Data: Deferred Data Guide。
再次忍不住发表个人观点:为什么要加这个功能?是为了给 Data API “擦屁股”。
由于引入了 loader,内部有 API 请求,必然导致路由切换时,页面需要时间去加载。加载时间长了怎么办?需要展示 Loading 态。
解决方案一:不要在 loader 内发 API 请求,在 Route 对应的 element 里发请求,并展示 Loading 态。React Router 提供了贴心的 useFetcher,可以在element内发请求。解决方案二:针对 loader,提供一种配置方案,允许开发者定义 Loading 态。React Router 这两种方案都提供了。方案一就是 useFetcher。为了实现方案二,它引入了defer
函数和
组件。
3.1 defer 函数
在 loader 内使用,表明这个 loader 需要展示 Loading 态。如果 loader 返回了 defer,那么就会直接渲染
的 element。
{ let book = await getBook(); // 这个不会展示 Loading 态,因为它被 await 了,会等它执行完并拿到数据 let reviews = getReviews(); // 这个会展示 Loading 态 return defer({ book, // 这是数据 reviews, // 这是 promise }); }} element={ }/>;
3.2
组件
在
的 element 中使用,用于展示 Loading 态。需要结合
使用,Loading 态展示在
的 fallback 中。
function Book() { const { book, reviews, // this is the same promise } = useLoaderData(); return ( {book.title}
{book.description}
}> /> );}
等 loader 加载完毕,就会展示 Await 的 children 里的内容了。
3.2.1
组件的 children 属性
可以是函数,也可以是 React 组件。
如果是函数,Promise 结果就是参数:
{(resolvedReviews) => }
如果是组件,内部通过useAsyncValue
获取 Promise 的结果。
;function Reviews() { const resolvedReviews = useAsyncValue(); return {/* ... */};}
个人观点
重申我的核心观点:React Router 6.4 不再是纯粹的路由组件了,它耦合了数据获取逻辑。
如果一个庞大项目,一些数据获取逻辑在 Router 里,一些数据获取逻辑在内部组件。这不利于项目维护。React Router 6.4 为了加个 Data API,增加了很多代码。v6.4 打包UMD production.min.js 体积(16.1KB) 是 v6.3 打包UMD production.min.js(6.75KB) 体积的 2.4 倍!打包测试
公共依赖:
"react": "^18.2.0","react-dom": "^18.2.0","react-scripts": "5.0.1",
不引用 React Router
下面代码打包后,141199 B。
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);root.render( ,);
React Router v6.3
下面代码打包后,150266 B。
import React from "react";import ReactDOM from "react-dom/client";import { BrowserRouter, Routes, Route } from "react-router-dom";const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);root.render( } /> ,);
React Router v6.4 不用 Data API
代码跟上面一致,159758 B。
React Router v6.4 使用 Data API
下面代码打包后,196040 B。
import React from "react";import ReactDOM from "react-dom/client";import { createBrowserRouter, RouterProvider,} from "react-router-dom";import "./index.css";const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);const router = createBrowserRouter([ { index: true, element: , },]);root.render( ,);
对比
打包物 | 体积(B) | 增长的体积(B) | 相对6.3增长倍数 | React Router占总代码比例 |
---|---|---|---|---|
无 React Router | 141199 | 0 | - | - |
React Router 6.3 | 150266 | 9067 | 1倍 | 6% |
React Router 6.4 不用 Data API | 159758 | 18559 | 2.05倍 | 12% |
React Router 6.4 使用 Data API | 196040 | 54841 | 6.05倍 | 28% |
结论
最终,我愿意使用 react-router-dom=~6.3.0
,即不更新到 6.4,永远使用 6.3.x。
毕竟,我的《联机桌游合集》里,没有http请求。我只想用一个纯粹的路由组件。而且6.4针对6.3的其它小feature,我也完全用不到。
React Router 最新版 文档链接: https://reactrouter.com/en/mainReact Router 6.3.0 文档链接: https://reactrouter.com/en/v6.3.0写在最后
我是HullQin,公众号线下聚会游戏的作者(欢迎关注我,交个朋友)。转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩UNO、斗地主、五子棋、飞行棋、一夜狼、象棋、德国心脏病、达芬奇密码等游戏,不收费无广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这个专栏里分享:《教你做小游戏》。
标签:
相关文章
4k字介绍 React Router 6.4 超大变化:引入 Data API。你不纯粹了!|世界观天下
每次打开ReactRouter官方文档,都会有惊吓,API又又又变了!这次看看有什么更新。
杨丞琳看时装周不知道名牌英文发音现尴尬样,网民批不专业又没礼貌 当前头条
台湾女星杨丞琳月初受名牌巴黎世家(Balenciaga)邀请,专程飞往巴黎参加时装周,但于场内拍片介绍一身美丽高贵的礼服及配饰时,却连巴黎世家
阿尔泰山在哪里 环球关注
阿尔泰山脉位于中国新疆维吾尔自治区北部和蒙古西部。西北延伸至俄罗斯境内。呈西北—东南走向,斜跨中国、哈萨克斯坦、俄罗斯、蒙古国境,...
查开fang记录网站-开房网 当前热点
1、开房?我不太肯定我说的是否是你想要的,你可这手机上安装交友APP,此类型的利用挺多。2、可以在市场里找的到,祝你开房愉快。本文到此分享
美菱家电售后怎么样_美菱家电 环球热推荐
随着科学技术的不断进步,我们的生活发生了巨大的变化。因为科学技术,现在我们使用越来越多的电器。电器的种类增加了,这给我们
全球新动态:贵港市港南区:“旱改水”为粮食安全保驾护航
广西新闻网贵港3月25日讯(通讯员曾钎祥黄颖谭天力)近年来,贵港市港南区坚持党建引领,高位推进“旱改水”等项目,有效破解土地撂荒难题...
当前关注:咸阳高新区召开2023年工业经济发展工作会
3月25日上午,咸阳高新区召开2023年工业经济发展工作会,区内158家企业代表、渭滨街道办、西吴街道办及阜寨镇相关负责人参加,
奔驰glb220怎么样值得购买吗(glb 220哪个更值得入手)
奔驰GLB车型一直以来都因为1 3T四缸机+7速双离合变速箱的弱鸡动力遭到大家的一致群嘲,而后经过了数年的痛定思痛,奔驰顶着各路嘲讽痛改前非,
天天讯息:推特将于4月1日起取消传统的蓝V认证标记
财经网科技3月25日讯,据界面新闻消息,TwitterInc 表示,将从4月1日开始取消那些在马斯克接管该平台之前验证过帐户的用户的免费蓝V认证标记。
国内发展比较外汇平台哪些?外汇平台排名一览-全球观察
国内发展比较外汇平台哪些?外汇平台排名一览,国内发展比较外汇平台哪些?外汇平台排名一览外汇平台排名一览:①Tri拓利外汇。②GMI。③嘉盛。
三十而已中的梁正贤扮演者|快看
1、三十而已中的梁正贤扮演者马志威。2、马志威,1985年8月31日出生于中国香港,中国香港男演员、模特,代表作品有《卧底巨星》。登上MILK、东
实时:广东累计培育超860家专精特新“小巨人”企业 2020—2022年,广东安排逾36亿元促进小微工业企业发展
广东累计培育超860家专精特新“小巨人”企业2020—2022年,广东安排逾36亿元促进小微工业企业发展人工智能朗读:南方日报讯(记者 唐亚冰...
中央气象台:华南中东部有强降水和强对流天气_天天热门
华南中东部有强降水和强对流天气一、天气实况1 国内实况昨日江西福建广东等地出现较强降雨昨08时至今06时,青海东北部、甘肃东部、山西北部、
英国决定向乌克兰提供贫铀弹遭多方批评
自英国国防部3月21日宣布向乌克兰输送的主战坦克配备弹药中将包含贫铀弹后,国际舆论纷纷谴责。俄罗斯外交部发言人扎哈罗娃23日向英国发出警告
环球滚动:梦见头发被剪了很漂亮是什么意思_梦见头发被剪了
1、梦见剪发:头发很烦恼,很纠结,所以梦见剪发是为了清理烦恼,是个好兆头。2、梦见自己剪了头发,说明你要和至亲好友或亲人吵架,责任在你
罗源县洪洋乡扎实推进“四上”企业及投资项目入库入统工作
福州新闻网3月24日讯今年以来,罗源县洪洋乡积极融入县委、县政府“建设现代化美丽海湾城市”2023年攻坚行动,按照罗源县奋进全国百强县指...
当前讯息:深圳住建局印发《深圳市住房公积金贷款管理规定》
新京报贝壳财经讯深圳市住房和建设局印发《深圳市住房公积金贷款管理规定》,单独申请的公积金贷款最高额度为50万元,申请人和计算可贷额度的
成都灵活就业人员医保生育时可不可以报销生育保险?-今日热讯
离职后以灵活就业人员身份参加成都市职工医保,生育时可不可以报销生育保险?灵活就业参保人员不属于职工生育保险参保范围,但参加了成都市职工
武汉的特色美食有哪些 焦点热文
1、热干面:武汉热干面与山西的刀削面、两广的伊府面、四川的担担面、北方的炸酱面并称为中国五大名面,是武汉地区的传统小吃之一。据说,上个
教育部等五部门:禁止中小学校举办或参与举办培训机构
人民网北京3月24日电(记者郝孟佳)近日,教育部办公厅、财政部办公厅、科技部办公厅、文化和旅游部办公厅、体育总局办公厅联合印发了《校外培
如何增加淘气值到2000_如何增加淘气值_天天亮点
1、打开手机淘宝,找到底部“”点击打开,如图;2、找到昵称下方的“”位置,点击打开,如图;3、这里可以看到自己的“”,如
实朴检测:东北证券、申万宏源等多家机构于3月22日调研我司-快资讯
2023年3月24日实朴检测301228发布公告称东北证券廖浩祥申万宏源张婧玮中银资管王瑾国联证券田伊依中信建投籍星博华夏未来资本曹迪雅于2023年3
赛百味为了「便宜」,导致不便宜 全球热点
01最近,赛百味又又又又传要出售股权消息,估价一百亿美金。说到赛百味,大部分人的第一印象,离不开“健康”、“生冷”、“在国内市场过的...
旅游 | 第二届湖南旅发大会主题口号、LOGO和吉祥物征集3月底截止
自2023年1月31日第二届湖南旅发大会主题口号、旅游形象标识(LOGO)和大会吉祥物作品征集活动启动以来,在全国乃至海外引发广泛关注,应征作品
森田药妆面膜怎么样要洗吗
森田药妆面膜有丰富多彩的精粹,具备强力补水保湿功效,也由于精粹较为多,因此敷完必须要洁面,肌肤对营养成分的消化吸收是不足的,当补水面
世界热点评!3月24日生意社环氧树脂基准价为14733.33元/吨
3月24日,生意社环氧树脂基准价为14733 33元 吨,与本月初(15100 00元 吨)相比,下降了-2 43%。环氧树脂年度统计(2022-03-24-