1. Введение
React Router v6вышла альфа, прошла на этой неделеA Sneak Peek at React Router v6В статье анализируются произошедшие изменения.
2 Обзор
Переименовать в
Безболезненное изменение, позволяющее сделать именование API более стандартным.
// v5
import { BrowserRouter, Switch, Route } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/profile">
<Profile />
</Route>
</Switch>
</BrowserRouter>
);
}
В React Router v6 используйте напрямуюRoutes
заменятьSwitch
:
// v6
import { BrowserRouter, Routes, Route } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile/*" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
Обновить
В версии v5 не интуитивно понятно передавать параметры компонентам, для прохождения нужно использовать RenderPropsrouteProps
:
import Profile from './Profile';
// v5
<Route path=":userId" component={Profile} />
<Route
path=":userId"
render={routeProps => (
<Profile {...routeProps} animate={true} />
)}
/>
// v6
<Route path=":userId" element={<Profile />} />
<Route path=":userId" element={<Profile animate={true} />} />
А в версии v6,render
а такжеcomponent
План был объединен вelement
Схема, которая может легко передавать реквизит без прозрачной передачиroteProps
параметр.
Более удобная вложенная маршрутизация
В v5 вложенные маршруты должны проходитьuseRouteMatch
получатьmatch
, и пройтиmatch.path
Сращивание реализует подмаршрутизацию:
// v5
import {
BrowserRouter,
Switch,
Route,
Link,
useRouteMatch
} from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/profile" component={Profile} />
</Switch>
</BrowserRouter>
);
}
function Profile() {
let match = useRouteMatch();
return (
<div>
<nav>
<Link to={`${match.url}/me`}>My Profile</Link>
</nav>
<Switch>
<Route path={`${match.path}/me`}>
<MyProfile />
</Route>
<Route path={`${match.path}/:id`}>
<OthersProfile />
</Route>
</Switch>
</div>
);
}
опущен в версии v6useRouteMatch
Этот шаг поддерживает прямое использованиеpath
Представляет относительный путь:
// v6
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";
// Approach #1
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile/*" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
function Profile() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Routes>
<Route path="me" element={<MyProfile />} />
<Route path=":id" element={<OthersProfile />} />
</Routes>
</div>
);
}
// Approach #2
// You can also define all
// <Route> in a single place
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>
<Outlet />
</div>
);
}
УведомлениеOutlet
это элемент, который отображает дочерний маршрут.
useNavigate заменяет useHistory
В версии v5 активная джамп-маршрутизация может проходить черезuseHistory
провестиhistory.push
и так далее:
// v5
import { useHistory } from "react-router-dom";
function MyButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return <button onClick={handleClick}>Submit</button>;
}
В версии v6 вы можете пройтиuseNavigate
Непосредственно реализуйте эту общую операцию:
// v6
import { useNavigate } from "react-router-dom";
function MyButton() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return <button onClick={handleClick}>Submit</button>;
}
react-router внутренне инкапсулирует историю, если это необходимоhistory.replace
, в состоянии пройти{ replace: true }
Спецификация параметра:
// v5
history.push("/home");
history.replace("/home");
// v6
navigate("/home");
navigate("/home", { replace: true });
Меньший размер 8kb
Из-за почти рефакторинга кода сжатый размер v6 версии кода был уменьшен с 20kb до 8kb.
3 Интенсивное чтение
В исходном коде react-router v6 есть относительно основная концепция, автор взял ее и поделился с вами, что очень полезно для разработки некоторых фреймворков. мы видимuseRoutes
Этот код выдержки:
export function useRoutes(routes, basename = "", caseSensitive = false) {
let {
params: parentParams,
pathname: parentPathname,
route: parentRoute
} = React.useContext(RouteContext);
if (warnAboutMissingTrailingSplatAt) {
// ...
}
basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;
let navigate = useNavigate();
let location = useLocation();
let matches = React.useMemo(
() => matchRoutes(routes, location, basename, caseSensitive),
[routes, location, basename, caseSensitive]
);
// ...
// Otherwise render an element.
let element = matches.reduceRight((outlet, { params, pathname, route }) => {
return (
<RouteContext.Provider
children={route.element}
value={{
outlet,
params: readOnly({ ...parentParams, ...params }),
pathname: joinPaths([basename, pathname]),
route
}}
/>
);
}, null);
return element;
}
Видно, что с помощьюReact.Context
, версия v6 оборачивает слой при отображении каждого элемента маршрута.RouteContext
.
В качестве примера возьмем более удобную вложенность маршрутов:
опущен в версии v6
useRouteMatch
Этот шаг поддерживает прямое использованиеpath
Представляет относительный путь.
Это достигается с помощью этой схемы, потому что каждый уровень файлов маршрутизации обернут контекстом, поэтому каждый уровень может получить предыдущий уровень.path
, поэтому при сплайсинге маршрутов его можно полностью реализовать внутри фреймворка, без необходимости пользователям предварительно сплайсировать его при вызове.
снова сuseNavigate
Например, некоторые считаютnavigate
Эта инкапсуляция остается только на формальном уровне, но она также имеет инкапсуляцию в функции.Например, если она передается по относительному пути, она будет переключаться в соответствии с текущим маршрутом.СледующееuseNavigate
Выдержка из кода:
export function useNavigate() {
let { history, pending } = React.useContext(LocationContext);
let { pathname } = React.useContext(RouteContext);
let navigate = React.useCallback(
(to, { replace, state } = {}) => {
if (typeof to === "number") {
history.go(to);
} else {
let relativeTo = resolveLocation(to, pathname);
let method = !!replace || pending ? "replace" : "push";
history[method](relativeTo, state);
}
},
[history, pending, pathname]
);
return navigate;
}
Видно, что с помощьюRouteContext
получить токpathname
, и согласноresolveLocation
правильноto
а такжеpathname
выполнить сращивание путей, в то время какpathname
черезRouteContext.Provider
который предоставил.
Умелое использование многоуровневых контекстных провайдеров
Много раз мы используем Context, чтобы оставаться вProvider
, несколькоuseContext
На уровне контекста это самое основное использование контекста, но я считаю, что после прочтения React Router v6 мы сможем узнать больше об использовании контекста: многоуровневый поставщик контекста.
Хотя Context Provider имеет несколько уровней и будет использовать принцип ближайшего покрытия, это не просто функция, позволяющая избежать ошибок, мы можем использовать эту функцию для достижения улучшений, таких как React Router v6.
Чтобы более подробно объяснить эту функцию, вот еще один конкретный пример: Например, при создании механизма рендеринга у каждого компонента есть идентификатор, но этот идентификатор не отображается в свойствах компонента:
const Input = () => {
// Input 组件在画布中会自动生成一个 id,但这个 id 组件无法通过 props 拿到
};
На этом этапе, если мы разрешаем создание другого дочернего элемента внутри компонента Input и хотим, чтобы идентификатор этого дочернего элемента был получен из Input, нам может потребоваться, чтобы пользователь сделал следующее:
const Input = ({ id }) => {
return <ComponentLoader id={id + "1"} />;
};
При этом есть две проблемы:
- Воздействие компонента INPUT и нарушает простой дизайн предыдущего дизайна.
- Компонент должен собрать идентификатор, что очень хлопотно.
Здесь возникает та же проблема, что и в React Router.Можем ли мы упростить код до следующего, но функция останется прежней?
const Input = () => {
return <ComponentLoader id="1" />;
};
Ответ заключается в том, что это можно сделать, мы можем использовать Context для достижения этого решения. Ключевым моментом является то, что рендеринг Input, но контейнер компонента должен обернуть Provider:
const ComponentLoader = ({ id, element }) => {
<Context.Provider value={{ id }}>{element}</Context.Provider>;
};
Затем для внутренних компонентов, вызывающих на разных уровняхuseContext
Полученный идентификатор отличается, а это именно то, что нам нужно:
const ComponentLoader = ({id,element}) => {
const { id: parentId } = useContext(Context)
<Context.Provider value={{ id: parentId + id }}>
{element}
</Context.Provider>
}
по этому мыInput
внутренне называется<ComponentLoader id="1" />
Фактический идентификатор, который фактически объединен,01
, а это полностью брошено на внешний движок обработки слоя, пользователям не нужно прошивать вручную.
4 Резюме
После полного рефакторинга React Router v6 с помощью хуков не только значительно сократилось количество кода, но и стало проще его использовать, его можно быстро обновить при выходе официальной версии.
Кроме того, из этих оптимизаций, сделанных React Router v6, мы обнаружили более изобретательное использование контекста из исходного кода Я надеюсь, что этот метод поможет вам применить его к другим более сложным проектам.
Адрес обсуждения:Готовый "React Router V6" · Выпуск №241 · DT-Fe / Еженедельник
Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам. Интерфейс интенсивного чтения — поможет вам отфильтровать надежный контент.
Сфокусируйся наАккаунт WeChat для интенсивного чтения в интерфейсе
Заявление об авторских правах: Бесплатная перепечатка - некоммерческая - не производная - сохранить авторство (Лицензия Creative Commons 3.0)