react路由

v6

来源

react-router vue-router

  • SPA 出现后, 前端才开始自己接管路由
    • 现在,客户端接管了路由。
  • router 是啥
    • 路由的变化,是不是就是意味着界面(部分内容)的变化
    • 界面的变化,意味着数据的变化,组件的变化。
    • keywords: context, components, path(/user/edit)
  • 路由的核心
    • 根据/监听 path/url 的变化,根据 path 和 components 之间的对应关系,触发组件进行unmount 和 mount,同时使用 context 注入上下文
      • navigation
      • path

react-router

提供一些核心的 API, 如:Router, Route 这些,但是不提供和 DOM 相关的

react-router-dom

提供 BrowserRouter, HashRouter, Link 这些 API,可以通过 DOM 操作触发事件,控制路由

history

模拟浏览器的 history 的一个库, V6 的版本这个库已经内置的,并且导出成 navigation

react-router-dom v6

  • 已经没有 component 了,用 element 代替
Routes

一组路由,代替原来的 Switch

Route

基础路由

Link
Outlet

类似于 Vue 中的 router-view

useRoutes、useParams、useNavigate(代替以前的 useHistory)、useLocation
import { BrowserRouter, Outlet, Route, Routes, useLocation, useNavigate, useParams, useRoutes } from 'react-router-dom';
import { lazy,Suspense  } from 'react';
import { Link } from 'react-router-dom';

//写法1
function App_old() {
  return <BrowserRouter>
    <Routes>
      <Route path="/list" element={<div>新闻列表--pages</div>} />
      <Route path="/about" element={<div>关于我们--pages</div>} />
      <Route path="/hot" element={<div>热点新闻--pages</div>} />
    </Routes>
  </BrowserRouter>
}
//写法2
const NavMenu = ({ to, children }) => (
  <div className=' border-x border-gray-500 border-solid px-10 py-1 whitespace-nowrap'>
    <Link to={to} >{children}</Link>
  </div>
)

const Menu = () => (
  <div>
    <header className=' w-full h-14 flex flex-row justify-start items-center bg-blue-200 '>
      <NavMenu to="./" >首页</NavMenu>
      <NavMenu to="./list" >新闻列表</NavMenu>
      <NavMenu to="./about" >关于我们</NavMenu>
      <NavMenu to="./hot" >热点新闻</NavMenu>
    </header>
    <Outlet />
  </div>
)
const List = () => {
  const nav = useNavigate();
  return <div>
    this is the news Page
    <button onClick={() => nav('/post/1')} >去新闻1</button>
  </div>
}
const Post = () => {
  const { id } = useParams();
  const location = useLocation();
  console.log(location)
  return <div>
    {id}: 新闻信息
  </div> 
}

const App_old_Outlet = () => {
  return <BrowserRouter>
    <Routes>
      <Route path='/' element={<Menu />}>
        <Route path='/list' element={<List />} />
        <Route path='/about' element={<div>关于我们--pages</div>} />
        <Route path="/hot" element={<div>热点新闻--pages</div>} />
      </Route>
    </Routes>
  </BrowserRouter>
}
 
 //写法3
 let routes = [
  {
    path: '/', element: <Menu />,
    children: [
      { path: '/list', element: <List />},
      { path: '/post/:id', element: <Post />},
      { path: '/about', element: <div>关于我们--pages--json</div>},
      { path: '/hot', element: <div>
        <Suspense fallback={<div>loading</div>}>
          <DynamicNews />
        </Suspense>
      </div>},
    ]
  }
]

const Routing = () => useRoutes(routes)
 const App = () => {
  return <BrowserRouter>
    <Routing />
  </BrowserRouter>
}
手写router实现
import { createBrowserHistory, createHashHistory } from "history";
import { useLayoutEffect, useState } from "react";
import { useMemo } from "react";
import { useRef } from "react";
import { useContext } from "react";
import React,  { createContext } from "react";


// 创建上下文
const NavigationContext = createContext({});
const LocationContext = createContext({});

export function HashRouter({ children }) {
    let historyRef = useRef();
    if(historyRef.current == null) {
       historyRef.current = createHashHistory();
    };

    let history = historyRef.current;

    let [ state, setState ] = useState({
       action: history.action,
       location: history.location
    })
    use

   //  我们需要监听 history 的变化, 用useLayoutEffect
   // 当 history 变化的时候,(浏览器输入、获取a标签跳转,api跳转)
   // 派发更新,渲染整个 router 树
   useLayoutEffect(() => history.listen(setState), [history]);
   return <Router 
       children={children}
       location={state.location}
       navigator={history}
       navigationType={state.action}
   />
}

export function BrowserRouter({ children }) {
     let historyRef = useRef();
     if(historyRef.current == null) {
        historyRef.current = createBrowserHistory();
     };

     let history = historyRef.current;

     let [ state, setState ] = useState({
        action: history.action,
        location: history.location
     })

    //  我们需要监听 history 的变化, 用useLayoutEffect
    // 当 history 变化的时候,(浏览器输入、获取a标签跳转,api跳转)
    // 派发更新,渲染整个 router 树
    useLayoutEffect(() => history.listen(setState), [history]);
    return <Router 
        children={children}
        location={state.location}
        navigator={history}
        navigationType={state.action}
    />
}

// Provider 
function Router({ children, location: locationProp, navigator}) {
    const navigationContext = useMemo(() => ({ navigator }), [navigator]);
    const locationContext = useMemo(() => ({ location: locationProp }), [locationProp])
    return <NavigationContext.Provider value={navigationContext}>
        <LocationContext.Provider
            value={locationContext}
            children={children}
        >
        </LocationContext.Provider>
    </NavigationContext.Provider>
}

function useLocation() {
    return useContext(LocationContext).location;
}

function useNavigate() {
    return useContext(NavigationContext).navigator;
}

// const Routing = () => useRoutes(routes)
// Routing 就是 我跟你 routes 参数的内容,
// 结合当前浏览器上的 url, 返回具体是哪个 element
function useRoutes(routes) {
    let location = useLocation(); // 当前路径
    let currentPath = location.pathname || '/';
    console.log(currentPath)
    for(let i = 0; i < routes.length; i++) {
        let { path, element } = routes[i];
        let match = currentPath.match(new RegExp(`^${path}`));
        if(match) {
            return element;
        }
    }
    return null;
}
// Routes 这个东西,
// 我就是要把所有的 Route 组件,创建成一棵树
export const Routes = ({ children }) => 
    useRoutes(createRoutesFromChildren(children));

export const Route = () => {}

// 我就是要把 <Route /> 的嵌套,转成一棵树。
export const createRoutesFromChildren = (children) => {
    let routes = [];
    React.Children.forEach(children, (node) => {
        let route = {
            element: node.props.element,
            path: node.props.path,
        };

        if(node.props.children) {
            route.children = createRoutesFromChildren(node.props.children)
        }

        routes.push(route);
    });
    console.log(routes)
    return routes;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值