component/render
被element
替代Routes
和Route
基于当前位置在React Router
中渲染某些内容的主要方法src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Profile from './components/Profile';
ReactDOM.render(
<Router>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/user">User</Link></li>
<li><Link to="/profile">Profile</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/user" element={<User />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Router>,
document.getElementById('root')
);
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Profile from './components/Profile';
ReactDOM.render(
<Router>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/user">User</Link></li>
<li><Link to="/profile">Profile</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
+ <Route path="/user/*" element={<User />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Router>,
document.getElementById('root')
);
src\components\User.js
import React from 'react';
+import { Route, Routes, Link } from 'react-router-dom';
+import UserAdd from './UserAdd';
+import UserDetail from './UserDetail';
+import UserList from './UserList';
function User() {
return (
+ <div>
+ <ul>
+ <li><Link to="add">添加用户</Link></li>
+ <li><Link to="list">用户列表</Link></li>
+ </ul>
+ <Routes>
+ <Route path="add" element={<UserAdd />} />
+ <Route path="list" element={<UserList />} />
+ <Route path="detail/:id" element={<UserDetail />} />
+ </Routes>
+ </div>
+ )
}
export default User;
src\components\UserAdd.js
import React from 'react';
function UserAdd() {
return (
<div>UserAdd</div>
)
}
export default UserAdd;
src\components\UserDetail.js
import React from 'react';
function UserDetail() {
return (
<div>UserDetail</div>
)
}
export default UserDetail;
src\components\UserList.js
import React from 'react';
function UserList() {
return (
<div>UserList</div>
)
}
export default UserList;
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Profile from './components/Profile';
import UserAdd from './components/UserAdd';
import UserDetail from './components/UserDetail';
import UserList from './components/UserList';
ReactDOM.render(
<Router>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/user">User</Link></li>
<li><Link to="/profile">Profile</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
+ <Route path="/user" element={<User />} >
+ <Route path="add" element={<UserAdd />} />
+ <Route path="list" element={<UserList />} />
+ <Route path="detail/:id" element={<UserDetail />} />
+ </Route>
<Route path="/profile" element={<Profile />} />
</Routes>
</Router>,
document.getElementById('root')
);
src\components\User.js
import React from 'react';
+import { Link, Outlet } from 'react-router-dom';
function User() {
return (
<div>
<ul>
<li><Link to="add">添加用户</Link></li>
<li><Link to="list">用户列表</Link></li>
</ul>
+ <Outlet />
</div>
)
}
export default User;
useNavigate
钩子返回一个函数,该函数允许您以编程方式进行导航src\components\Home.js
import React from 'react';
import { useNavigate } from 'react-router-dom';
function Home() {
+ let navigate = useNavigate();
+ function navigateToUser() {
+ navigate('/user');
+ };
+ return (
+ <div>
+ <p>Home</p>
+ <button onClick={navigateToUser}>跳转到/user</button>
+ </div>
+ )
}
export default Home;
import React from 'react';
import ReactDOM from 'react-dom';
+import { BrowserRouter as Router, Route, Routes, Link, useRoutes } from 'react-router-dom';
+import routes from './routes';
+function App() {
+ return useRoutes(routes);
+}
ReactDOM.render(
<Router>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/user">User</Link></li>
<li><Link to="/profile">Profile</Link></li>
</ul>
+ <App />
</Router>,
document.getElementById('root')
);
src\routes.js
import Home from './components/Home';
import User from './components/User';
import Profile from './components/Profile';
import UserAdd from './components/UserAdd';
import UserDetail from './components/UserDetail';
import UserList from './components/UserList';
import NotFound from './components/NotFound';
const routes = [
{ path: '/', element: <Home /> },
{ path: '/profile', element: <Profile /> },
{
path: 'user',
element: <User />,
children: [
{ path: 'add', element: <UserAdd /> },
{ path: 'list', element: <UserList /> },
{ path: 'detail/:id', element: <UserDetail /> }
]
},
// 404找不到
{ path: '*', element: <NotFound /> }
];
export default routes;
Navigate
元素在渲染时更改当前位置import { HashRouter as Router, Route, Routes, Navigate } from 'react-router-dom'
<Router>
<Routes>
<Route path='/login' element={<Login/>}/>
<Route path='/admin' element={<Admin/>}/>
<Route path="*" element={<Navigate to="/login" />} />
</Routes>
</Router>
public\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#root{
border:1px solid red;
}
</style>
</head>
<body>
<div id="root"></div>
<ul>
<li><a href="#/a">/a</a></li>
<li><a href="#/b">/b</a></li>
</ul>
<script>
window.addEventListener('hashchange',()=>{
console.log(window.location.hash);
let pathname = window.location.hash.slice(1);//把最前面的那个#删除
root.innerHTML = pathname;
});
</script>
</body>
</html>
history.pushState()
和history.replaceState()
,和1个事件window.onpopstate
history.forward
、history.back
、和history.go
触发,因为这些操作有一个共性,即修改了历史堆栈的当前指针onpopstate
事件History
栈,执行pushState
函数可压入设定的url
至栈顶,同时修改当前指针back
和forward
操作时,history栈大小并不会改变(history.length不变),仅仅移动当前指针的位置<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>browser</title>
</head>
<body>
<div id="root"></div>
<ul>
<li><a onclick="pushA()">/a</a></li>
<li><a onclick="pushB()">/b</a></li>
<li><a onclick="pushC()">/c</a></li>
<li><a onclick="forward()">forward</a></li>
<li><a onclick="back()">back</a></li>
<li><a onclick="go(-1)">go-1=back</a></li>
<li><a onclick="go(1)">go+1=forward</a></li>
</ul>
<script>
let history = window.history;
(function (history) {
let oldPushState = history.pushState;
history.pushState = function (state, title, pathname) {
let result = oldPushState.apply(history, arguments);
if (typeof window.onpushstate === 'function') {
window.onpushstate(new CustomEvent('pushstate', { detail: { pathname, state } }));
}
}
})(history);
window.onpushstate = (event) => {
console.log(event);
root.innerHTML = window.location.pathname;
}
window.onpopstate = (event) => {
console.log(event);
root.innerHTML = window.location.pathname;
}
function pushA() {
history.pushState({ name: 'a' }, null, '/a');
}
function pushB() {
history.pushState({ name: 'b' }, null, '/b');
}
function pushC() {
history.pushState({ name: 'c' }, null, '/c');
}
function forward() {
history.forward();
}
function back() {
history.back();
}
function go(step) {
history.go(step);
}
</script>
</body>
</html>
npm i react-router-dom --save
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Profile from './components/Profile';
ReactDOM.render(
<HashRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/user" element={<User />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</HashRouter>
, document.getElementById('root'));
src\components\Home.js
import React from 'react';
function Home(props) {
console.log(props);
return (
<div>Home</div>
)
}
export default Home;
src\components\User.js
import React from 'react';
function User() {
return (
<div>User</div>
)
}
export default User;
src\components\Profile.js
import React from 'react';
function Profile() {
return (
<div>Profile</div>
)
}
export default Profile;
src\react-router-dom\index.js
import React from 'react'
import { Router } from '../react-router';
import { createHashHistory, createBrowserHistory } from "history";
export * from '../react-router';
export function HashRouter({ children }) {
let historyRef = React.useRef();
if (historyRef.current == null) {
historyRef.current = createHashHistory();
}
let history = historyRef.current;
let [state, setState] = React.useState({
action: history.action,
location: history.location
});
React.useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
children={children}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
}
export function BrowserRouter({ children }) {
let historyRef = React.useRef();
if (historyRef.current == null) {
historyRef.current = createBrowserHistory();
}
let history = historyRef.current;
let [state, setState] = React.useState({
action: history.action,
location: history.location
});
React.useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
children={children}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
}
src\react-router\index.js
import React from 'react';
//导航上下文
const NavigationContext = React.createContext({});
//路径上下文
const LocationContext = React.createContext({});
//路由上下文
const RouteContext = React.createContext({});
export {
NavigationContext,
LocationContext,
RouteContext
};
export function Router({ children, location, navigator }) {
let navigationContext = React.useMemo(
() => ({ navigator }),
[navigator]
);
return (
<NavigationContext.Provider value={navigationContext}>
<LocationContext.Provider
children={children}
value={{ location }}
/>
</NavigationContext.Provider>
);
}
export function Routes({ children }) {
return useRoutes(createRoutesFromChildren(children));
}
export function useLocation() {
return React.useContext(LocationContext).location;
}
export function useRoutes(routes) {
let location = useLocation();//当前的路径对象
let pathname = location.pathname || "/";//当前的路径
for (let i = 0; i < routes.length; i++) {
let { path, element } = routes[i];
let match = matchPath(path, pathname);
if (match) {
return element;
}
}
return null;
}
export function createRoutesFromChildren(children) {
let routes = [];
React.Children.forEach(children, element => {
let route = {
path: element.props.path,
element: element.props.element
};
routes.push(route);
});
return routes;
}
export function Route(props) {
}
function compilePath(path) {
let source = "^" + path;
source += "$";
let matcher = new RegExp(source);
return matcher;
}
export function matchPath(path, pathname) {
let matcher = compilePath(path);
let match = pathname.match(matcher);
if (!match) return null;
return match;
}