Skip to content

Latest commit

 

History

History
226 lines (194 loc) · 5.45 KB

理解与补充.md

File metadata and controls

226 lines (194 loc) · 5.45 KB

react-router 存在两中路由方式

  1. 数据路由
  2. 组件路由

数据路由是由 createBrowserRoutercreateHashRoutercreateMemoryRouter 等这些函数构造而成

使用 create*这类的构造函数传入 routes 对象是一种嵌套类型的对象,这种嵌套反映了路由的父子关系去遍历生成 routes 类似于 useRoutes 对 routes 的处理

它会返回一个 route的对象

 router = {
    get basename() {
      return init.basename;
    },
    get state() {
      return state;
    },
    get routes() {
      return dataRoutes;
    },
    initialize,
    subscribe,
    enableScrollRestoration,
    navigate,
    fetch,
    revalidate,
    createHref: ,
    getFetcher,
    deleteFetcher,
    dispose,
    _internalFetchControllers: fetchControllers,
    _internalActiveDeferreds: activeDeferreds,
  };

这样使用

import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";import ()

const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    loader: () => {},
    children: [
      {
        path: 'team',
        element: <Team />,
        loader: () => {}
      }
    ]
  }
])

ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router}>
)

数据路由没有仔细看过这里,简单看一下组件路由

组件的使用方式

它会提供一个干净的路由将当前位置存储在浏览器的地址栏中,并使用浏览器内置的历史堆栈进行导航。

import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";

const root = createRoot(document.getElementById("root"));

root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

它的源码,比较简单,大致看一下

export function BrowserRouter({
   basename,
   children,
   window,
 }: BrowserRouterProps) {
   let historyRef = React.useRef<BrowserHistory>();
   if (historyRef.current == null) {
     historyRef.current = createBrowserHistory({ window, v5Compat: true });
   }
 
   let history = historyRef.current;
   // 创建一个 state 状态
   // 观察过 history 库可以明白 history.listen() 的参数会接收 {action, location}
   let [state, setState] = React.useState({
     action: history.action,
     location: history.location,
   });

   // 在 dom 改变之前去往 history 添加 setState 事件,每次 history 更新都要添加
   // 当 url 发生变化就会通过 history 库去触发 监听时间让整个组件更新
   React.useLayoutEffect(() => history.listen(setState), [history]);
 
   return (
     <Router
       basename={basename}
       children={children}
       location={state.location}
       navigationType={state.action}
       navigator={history}
     />
   );
 }

这里看一下 Router 的源码

export function Router({
  basename: basenameProp = "/",
  children = null,
  location: locationProp,
  navigationType = NavigationType.Pop,
  navigator,
  static: staticProp = false,
}: RouterProps): React.ReactElement | null {
  invariant(
    !useInRouterContext(),
    `You cannot render a <Router> inside another <Router>.` +
      ` You should never have more than one in your app.`
  );

  // 比如:giao/* 会处理成 giao
  let basename = basenameProp.replace(/^\/*/, "/");

  // 存在 context 中 
  let navigationContext = React.useMemo(
    () => ({ basename, navigator, static: staticProp }),
    [basename, navigator, staticProp]
  );
  
  // location 如果是字符串就要解析成 对象
  if (typeof locationProp === "string") {
    // 将字符串解析为 pathname, search, hash 属性的对象
    locationProp = parsePath(locationProp);
  }

  let {
    pathname = "/",
    search = "",
    hash = "",
    state = null,
    key = "default",
  } = locationProp;


  // 构建一个当前的 location
  let location = React.useMemo(() => {
    // 这里的函数表明 basename 一定被包含于 pathname 才行
    // 如果不是这样的关系就会返回 null ,如果 pathname === '/' 就直接返回 
    // 假如pathname: 'foo/name/www', basename: 'foo/' 那就会返回 foo/ 之后的 pathname 字符串
    let trailingPathname = stripBasename(pathname, basename);

    if (trailingPathname == null) {
      return null;
    }

    return {
      pathname: trailingPathname,
      search,
      hash,
      state,
      key,
    };
  }, [basename, pathname, search, hash, state, key]);

  warning(
    location != null,
    `<Router basename="${basename}"> is not able to match the URL ` +
      `"${pathname}${search}${hash}" because it does not start with the ` +
      `basename, so the <Router> won't render anything.`
  );

  if (location == null) {
    return null;
  }

  return (
    // NavigationContext 是一个 context 存储 navigationContext
    <NavigationContext.Provider value={navigationContext}>
      <LocationContext.Provider
        children={children}
        value={{ location, navigationType }}
      />
    </NavigationContext.Provider>
  );
}

接下来使用 useRoutes hook 来创建一些 匹配规则,匹配到响应的 url渲染相应的组件

import { useRoutes } from 'react-router-dom'

const routes =   {
    path: '/',
    element: <HomeLayout />,
    children: [
      {
        path: '',
        element: <Navigate to="spylist" />
      },
      // 实时信息
      {
        path: 'spylist',
        element: <SpyList />
      }
    ]
}

function App() {
  return useRoutes(routes)
}

export default App