目录
1. 前言
2. 变更概览
将 Switch 升级为 Routes
路由匹配组件参数 由 component 改为 element
相对路径识别(子路由不需要补全父路由的path,react会自动补全)
废弃 Redirect 标签,使用 Navigate 标签实现路由重定向
优化路由嵌套,添加 outlet 标签
使用 index 标识默认路由
添加 useResolvedPath hooks
添加 useSearchParams 读取和设置url参数
link 标签跳转的path 将支持 . 和 .. 这种语法(类比于 terminal 中的 cd .. 返回上级菜单 )
path 通配符将只支持 * 和 :(以前的?等将不再支持)
添加 useOutletContext 用于 路由之间共享状态
1. 前言
伴随React18的到来,React的路由也有 5.# 版本更新到了 V6版本,接下来让我们总结下 V6 版本的更新内容,对我们的使用有什么影响。
其实官网文档写的很清晰,我这里只是做个总结,大家也可直接移步官网: React Router | Overview https://reactrouter.com/docs/en/v6/getting-started/overview
https://reactrouter.com/docs/en/v6/getting-started/overview
2. 变更概览
-  将 Switch 升级为 Routes
-  路由匹配组件参数 由 component 改为 element
// before V6 
<Switch>
    <Route path="/home" component={Home}></Route>
</Switch>
// V6 
<Routes>
    // 注意,这里是 jsx 语法,需要配合标签, 传参也可以直接写为组件传参
    <Route path="/home" element={<Home animate={true} />}></Route>
</Routes>-  相对路径识别(子路由不需要补全父路由的path,react会自动补全)
<Routes>
   <Route path="user" element={<Invoices />}>
       <Route path=":id" element={<Invoice />} />
       <Route path="me" element={<SentInvoices />} />
   </Route>
</Routes>
// path: /user
// path: /user/:id
// path: /user/me-  用 useNavigate 替代 useHistory
// 函数组件使用编程式跳转
// V5
let history = useHistory();
history.push("/home");
// V6
let navigate = useNavigate();
navigate('/home')
// 如果需要类比 history.replace, 可以添加参数replace为true
navigate(to, { replace: true })
// 如果需要类比隐式传参,可以添加参数 state
navigate(to, { state })
// 同时 link 也添加了单独的参数 state
<Link to="/home" state={state} />
// 如果需要类比 goBack,go等语法,也可直接在 navigate中 传层级参数
// 等价于 history.go(-1)
<button onClick={() => navigate(-2)}>
    Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
    Go forward
</button>
<button onClick={() => navigate(2)}>
    Go 2 pages forward
</button>-  废弃 Redirect 标签,使用 Navigate 标签实现路由重定向
import { Navigate } from "react-router-dom";
function App() {
  return <Navigate to="/home" replace state={state} />;
}
/*
    v5默认<Redirect />使用 replace 逻辑
    v6默认<Navigate />使用 push 逻辑 ,可以通过参数设置为 replace
*/-  优化路由嵌套,添加 outlet 标签
import {
    Routes,
    Route,
    Link,
    Outlet,
    BrowserRouter
  } from "react-router-dom";
  
  function Layout() {
    return (
      <div>
        <h1>Welcome to the V6!</h1>
        <nav>
          <Link to="product">产品页</Link>
          <br/>
          <Link to="detail">详情页</Link>
        </nav>
        <div className="content">
  
          {/* 子路由将会显示在这里,用outlet占位 */}
          <Outlet />
  
        </div>
      </div>
    );
  }
  
  function Product() {
    return <h1>产品页</h1>;
  }
  
  function Detail() {
    return <h1>详情页</h1>;
  }
  function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Layout />}>
                    <Route path="product" element={<Product />} />
                    <Route path="detail" element={<Detail />} />
                </Route>
            </Routes>
        </BrowserRouter>
    );
  }
  
  export default App-  使用 index 标识默认路由
<Routes>
   <Route path="/" element={<Layout />}>
       <Route index element={<Activity />} />
       <Route path="invoices" element={<Invoices />} />
       <Route path="activity" element={<Activity />} />
   </Route>
</Routes>-  添加 useResolvedPath hooks
在 V5版本的文档中,只有四个比较重要的hooks,分别是 useHistory, useLocation, useParams, useRouteMatch,V5文档:
React Router: Declarative Routing for React.js https://v5.reactrouter.com/web/api/Hooks/useparams而 V6 版本又添加了一些hooks,我们简单列举几个可能会用到的,完整版移步官网:
https://v5.reactrouter.com/web/api/Hooks/useparams而 V6 版本又添加了一些hooks,我们简单列举几个可能会用到的,完整版移步官网:
https://reactrouter.com/docs/en/v6/api#resolvepath https://reactrouter.com/docs/en/v6/api#resolvepath
https://reactrouter.com/docs/en/v6/api#resolvepath
由于V6的相对路径识别特性,有时我们需要获取完整的url路径,可以使用 useRelovedPath
useRelovedPath 必须接收一个参数,可以为空字符串
  <Route path="/" element={<Layout />}>
       <Route path="product" element={<Product />} />
       <Route path="detail" element={<Detail />} />
  </Route>
  function Product() {
    const path = useResolvedPath('id');
    console.log(path);  // output: { pathname: '/product/id' }
    return <h1>产品页</h1>;
  }
  
  function Detail() {
    const path = useResolvedPath('');
    console.log(path);   // output: { pathname: '/detail' }
    return <h1>详情页</h1>;
  }-  添加 useSearchParams 读取和设置url参数
useSerachParams 可以读取和修改当前位置url的查询参数(?id=123), 具体使用方式类比于 useState,但用法略有不同。
获取某个searchParams: searchParams.get(key)
设置某个searchParams: setSearchParams({key:value})
import {
    Routes,
    Route,
    Link,
    Outlet,
    BrowserRouter,
    useResolvedPath,
    useSearchParams
  } from "react-router-dom";
  
  function Layout() {
    return (
      <div>
        <h1>Welcome to the V6!</h1>
        <nav>
          <Link to="product">产品页</Link>
          <Link to="detail?id=123">详情页</Link>
        </nav>
        <div className="content">
  
          <Outlet />
  
        </div>
      </div>
    );
  }
  
  function Product() {
    const path = useResolvedPath('id');
    console.log(path);
    return <h1>产品页</h1>;
  }
  
  function Detail() {
    const [searchParams,setSearchParams] = useSearchParams()
    const handleSubmit = ()=>{
        // 输入键值对,设置对应的 search 参数
        setSearchParams({id:456})
    }
    // 通过 get 方法获取key对应的value
    console.log(searchParams.get('id'));
    return (
        <h1>
            详情页 : {searchParams.get('id')} 
            <br/>
            <button onClick={()=>handleSubmit()}>update searchParams</button>
        </h1>
    );
  }
  function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Layout />}>
                    <Route path="product" element={<Product />} />
                    <Route path="detail" element={<Detail />} />
                </Route>
            </Routes>
        </BrowserRouter>
    );
  }
  
  export default App-  link 标签跳转的path 将支持 . 和 .. 这种语法(类比于 terminal 中的 cd .. 返回上级菜单 )
// 这里直接拿了官网的示例
function App() {
  return (
   <BrowserRouter>
     <Routes>
       <Route path="users" element={<Users />}>
         <Route path=":id" element={<UserProfile />} />
       </Route>
     </Routes>
   <BrowserRouter>
  );
}
function Users() {
  return (
    <div>
      <h2>
        {/* This links to /users - the current route */}
        <Link to=".">Users</Link>
      </h2>
      <ul>
        {users.map((user) => (
          <li>
            {/* This links to /users/:id - the child route */}
            <Link to={user.id}>{user.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}
function UserProfile() {
  return (
    <div>
      <h2>
        {/* This links to /users - the parent route */}
        <Link to="..">All Users</Link>
      </h2>
      <h2>
        {/* This links to /users/:id - the current route */}
        <Link to=".">User Profile</Link>
      </h2>
      <h2>
        {/* This links to /users/mj - a "sibling" route */}
        <Link to="../mj">MJ</Link>
      </h2>
    </div>
  );
}-  path 通配符将只支持 * 和 :(以前的?等将不再支持)
// 这里直接拿了官网的例子,让我们看下 * 的作用(子孙路由)
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
} from "react-router-dom";
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="users/*" element={<Users />} />
      </Routes>
    </BrowserRouter>
  );
}
function Users() {
  return (
    <div>
      <nav>
        // path: user/me
        <Link to="me">My Profile</Link>
      </nav>
      <Routes>
        // path:  user/:id
        <Route path=":id" element={<UserProfile />} />
        // path:  user/me
        <Route path="me" element={<OwnUserProfile />} />
      </Routes>
    </div>
  );
}-  添加 useOutletContext 用于 路由之间共享状态
我们可以用 useOutletContext 在子路由与父路由之间共享一些值
function Parent() {
  const [count, setCount] = React.useState(0);
  return <Outlet context={[count, setCount]} />;
}
import { useOutletContext } from "react-router-dom";
function Child() {
  const [count, setCount] = useOutletContext();
  const increment = () => setCount((c) => c + 1);
  return <button onClick={increment}>{count}</button>;
}

















