“Yeah It’s on. ”
正文
BrowserRouter
一个使用了 HTML5 history API 的高阶路由组件,保证你的 UI 界面和 URL 保持同步。此组件拥有以下属性:
basename: string 作用:为所有位置添加一个基准URL 使用场景:假如你需要把页面部署到服务器的二级目录,你可以使用 basename 设置到此目录。
<BrowserRouter basename="/minooo" />
<Link to="/react" /> // 最终渲染为 <a href="/minooo/react">
getUserConfirmation: func 作用:导航到此页面前执行的函数,默认使用 window.confirm 使用场景:当需要用户进入页面前执行什么操作时可用,不过一般用到的不多。
forceRefresh: bool 作用:当浏览器不支持 HTML5 的 history API 时强制刷新页面。 使用场景:同上。
const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory} />
keyLength: number
作用:设置它里面路由的 location.key 的长度。默认是6。(key的作用:点击同一个链接时,每次该路由下的 location.key都会改变,可以通过 key 的变化来刷新页面。)
使用场景:按需设置。
<BrowserRouter keyLength={12} />
children: node 作用:渲染唯一子元素。 使用场景:作为一个 React组件,天生自带 children 属性。
Link
https://stackoverflow.com/questions/42914666/react-router-external-link
With Link component of react-router you can do that. In the “to” prop you can specify 3 types of data:
-
a string: A string representation of the Link location, created by concatenating the location’s pathname, search, and hash properties.
-
- an object
-
An object that can have any of the following properties:
- pathname: A string representing the path to link to.
- search: A string representation of query parameters.
- hash: A hash to put in the URL, e.g. #a-hash.
- state: State to persist to the location.
- a function: A function to which current location is passed as an argument and which should return location representation as a string or as an object
Route
<Route>
也许是 RR4 中最重要的组件了,重要到你必须理解它,学会它,用好它。它最基本的职责就是当页面的访问地址与 Route 上的 path 匹配时,就渲染出对应的 UI 界面。
<Route>
自带三个 render method 和三个 props 。
render methods 分别是:
<Route component>
<Route render>
<Route children>
每种 render method 都有不同的应用场景,同一个<Route>
应该只使用一种 render method ,大部分情况下你将使用 component 。
props 分别是:
- match
- location
- history
所有的 render method 无一例外都将被传入这些 props。
在 Route component 中,以 this.props.location 的方式获取, 在 Route render 中,以 ({ location }) => () 的方式获取, 在 Route children 中,以 ({ location }) => () 的方式获取,
History 和 match的获取方式类似。
如果当前的地址是 /char/123
那么match.path是 /char/:user
那么match.url是 /chat/123
match.url是指向实际的url
ps:在这个例子中,match.params.user为123
component
只有当访问地址和路由匹配时,一个 React component 才会被渲染,此时此组件接受 route props (match, location, history)。 当使用 component 时,router 将使用 React.createElement 根据给定的 component 创建一个新的 React 元素。这意味着如果你使用内联函数(inline function)传值给 component 将会产生不必要的重复装载。对于内联渲染(inline rendering), 建议使用 render prop。
<Route path="/user/:username" component={User} />
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
render: func
此方法适用于内联渲染,而且不会产生上文说的重复装载问题。
// 内联渲染
<Route path="/home" render={() => <h1>Home</h1} />
// 包装 组合
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props} />
</FaseIn>
)} />
)
<FadingRoute path="/cool" component={Something} />
IndexRoute
TIP: 在 react-router-dom v6 中 IndexRoute 已经被干掉了,请用index属性代替
https://reactrouter.com/en/main/route/route#index
通常情况下,我们会建立如下情况的路由:
<Router>
<Route path="/" component={App}>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
其中 App 组件一般情况下是一个 layout,比如包含了 header、footer 或者其他内容,其下面的 route 会被嵌入到这个 App 中(它们将成为 App 的 children),但这样配置路由有一个问题,就是我们访问 http://localhost:3000/ 这个地址时,你会发现仅渲染了一个 App 的 layout 内容,Accounts 和 Statements 都没有被渲染,这种情况下我们一般会设置一个默认页,当访问 / 这个路由时显示这个默认页。由此就需要用到 IndexRoute 功能,修改一下路由如下所示:
<Router>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
如此配置后,我们再次访问 / 路由,你会发现页面渲染了 Home 组件的内容。这就是 IndexRoute 的功能,指定一个路由的默认页。
IndexRedirect
上面这种情况比较常见,还有一种非常常见的方式就是当我们尝试访问 / 这个路由时,我们想让其直接跳转到 ‘/Accounts’,直接免去了默认页 Home,这样来的更加直接。由此我们就需要 IndexRedirect 功能。考虑如下路由:
<Router>
<Route path="/" component={App}>
<IndexRedirect to="/accounts"/>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
这样设计路由后,我们再次访问 / 时,系统默认会跳转到 /accounts 路由。
- IndexRoute 一般情况下用于设计一个默认页且不改变 URL 地址,而 IndexRedirect 则是跳转默认地址且地址会发生改变。
- IndexRoute 指定一个组件作为默认页,而 IndexRedirect 指定一个路由地址作为跳转地址。
嵌套路由
const routerItems = [
<Route path="/" element={<RootPage />}>
<Route path="/main" element={<MainPage />}>
// 默认路由
<Route path="" element={<div>1111</div>} />,
<Route path="home" element={<HomePage />} />,
<Route path="1v1" element={<One2onePage />} />,
</Route>
,
<Route path="/login" element={<LoginPage />} />,
<Route path="/verify" element={<VerifyPage />} />,
<Route path="*" element={<NotFoundPage />} />
</Route>,
]
const router = createHashRouter(createRoutesFromElements(routerItems))
export const RouteContainer = () => {
return (
<RouterProvider
router={router}
future=
fallbackElement={<div>loading...</div>}
></RouterProvider>
)
}
React-Router v6
https://juejin.cn/post/6844904096059621389#heading-1
简化路径
React Router v6使用简化的路径格,仅支持2种占位符:动态:id
样式参数和\*
通配符
以下都是v6中的有效路由路径:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
/files-*
使用RegExp
正则匹配的路径将无效:
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
v6
中的所有路径匹配都将忽略URL上的尾部”/
“。实际上,<Route strict>
已被删除并且在v6中无效。这并不意味着您不需要使用斜杠。
Outlet
React Router v6 出现,用于处理嵌套路由
https://segmentfault.com/a/1190000023684163
例子:
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile" element={<Profile />}>
<Route path=":id" element={<MyProfile />} />
<Route path="me" element={<OthersProfile />} />
</Route>
</Routes>
</BrowserRouter>
);
}
function Profile() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
{/*
将直接根据上面定义的不同路由参数,渲染<MyProfile />或<OthersProfile />
*/}
<Outlet />
</div>
)
}
监听路由变化
useHistory 变为使用 useLocation 代替
https://reactrouter.com/en/main/hooks/use-location
补充
react-router 还是 react-router-dom?
https://www.syncfusion.com/blogs/post/react-router-vs-react-router-dom.aspx
在 React 的使用中,我们一般要引入两个包,react 和 react-dom,那么 react-router 和 react-router-dom 是不是两个都要引用呢?
非也,坑就在这里。他们两个只要引用一个就行了,不同之处就是后者比前者多出了 <Link>
<BrowserRouter>
这样的 DOM 类组件。
因此我们只需引用 react-router-dom 这个包就行了。当然,如果搭配 redux ,你还需要使用 react-router-redux。
如果你只开发一个web应用,使用react-router-dom就足够了。
If you are working on a web application, the better option is to use react-router-dom, because it contains all the necessary common components and features essential for routing in a web application. react-router-dom installs react-router as a dependency, so there is no need to re-install and import anything directly from the react-router.
滚动至顶部
https://juejin.cn/post/6844904093694033927#heading-5
在大多数情况下,您所需要做的只是“滚动到顶部”,因为您有一个较长的内容页面,该页面在导航到该页面时始终保持向下滚动。使用<ScrollToTop>
组件可以轻松处理此问题,该组件将在每次导航时向上滚动窗口:
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
然后将其放在您应用的顶部。
function App() {
return (
<Router>
<ScrollToTop />
<App />
</Router>
);
}
Switch和Routes
React中的Switch和Routes都是React Router库中的组件,用于路由控制和页面切换。
Switch组件是用于包裹Routes组件的,它的作用是当第一个匹配到URL的Route被发现后,就停止查找匹配并只渲染该Route对应的组件。换句话说,Switch只渲染第一个匹配到的Route组件。
Routes组件则用于定义路由规则,它包含了多个Route组件,并根据URL匹配到的路径渲染对应的组件。Routes会逐个匹配路径,直到找到与URL匹配的路径为止。
总结起来,Switch是用来选择要渲染的Route组件的,而Routes则是用来定义路由规则的。Switch只渲染第一个匹配到的Route组件,并忽略后续的匹配,而Routes则按照定义的顺序逐个匹配路径,直到找到与URL匹配的路径为止。
In react-router-dom v6, “Switch” is replaced by routes “Routes”
最佳实践
const routerItems = [
<Route path="/" element={<RootPage />}>
<Route path="/welcome" element={<WelcomePage />}></Route>,
<Route path="/main" element={<MainPage />} >
<Route path="" element={
// 默认路由
} />
<Route path="home" element={
<Homepage />
} />
<Route path="discovery" element={
<DiscoveryPage />
} />
<Route path="mine" element={
<MinePage />
} />
</Route>,
<Route path="/login" element={<LoginPage />} />,
<Route path="/verify" element={<VerifyPage />} />,
<Route path="*" element={<NotFoundPage />} />,
</Route>,
]
const router = createHashRouter(createRoutesFromElements(routerItems))
export const RouteContainer = () => {
return <RouterProvider router={router} future=></RouterProvider>
}
// RootPage.tsx
const RootPage = () => {
useAuth() // 鉴权
const nav = useNavigate() // tip:这里
useEffect(() => {
nav("welcome")
}, [])
return <div className={styles.rootPage}>
<Outlet></Outlet>
</div>
}
为什么 需要 <Route path="/" element={<RootPage />}>
这一层包裹,其他都作为children呢?
因为 useNavigate() may be used only in the context of a router component
useNavigate 只能在router 包裹里面使用, 所有当我们有 全局鉴权 全局路由钩子时,一定要使用RootPage 在 path="/"
包裹一层
问题
location.hash after 6.7.0
https://github.com/remix-run/react-router/discussions/9973