react-router-v6原理解析 - 分支渲染

699 阅读1分钟

分支渲染

  • 当通过 matchRouteBranch 得到匹配的branches之后, 即可通过 _renderMatches 来得到需要渲染的element以及对嵌套路由的处理.

Outlet: 消费者

  • <Outlet> 用于渲染嵌套的子路由 , 功能类似于 vue-router 中的 <router-view>
export function Outlet(props: OutletProps): React.ReactElement | null {
  return useOutlet(props.context);
}
useOutlet
  • 负责消费 RouteContext
export function useOutlet(context?: unknown): React.ReactElement | null {
  let outlet = React.useContext(RouteContext).outlet;
  if (outlet) {
    return (
      <OutletContext.Provider value={context}>{outlet}</OutletContext.Provider>
    );
  }
  return outlet;
}

renderMatches: 提供者

  • branches 转换成 渲染的 element
  1. 每次的累加结果都将作为下次循环中的 outlet
  2. children渲染的element,可以使用 <Outlet> 来消费 <RouteContext.Provider>提供的outlet(其实就是渲染嵌套的子路由)
export function _renderMatches(
  matches: RouteMatch[] | null,
  parentMatches: RouteMatch[] = []
): React.ReactElement | null {
  if (matches == null) return null;
  return matches.reduceRight((outlet, match, index) => {
    return (
      <RouteContext.Provider
        children={
          match.route.element !== undefined ? match.route.element : outlet
        }
        value={{
          outlet,
          matches: parentMatches.concat(matches.slice(0, index + 1)),
        }}
      />
    );
  }, null as React.ReactElement | null);
}

一个🌰

  1. parent 路径下存在两个子路由(sub1和sub2)
// 根组件
function App(){
	return  <Routes>
        <Route path="/parent" element={<Parent />}>
              <Route path="sub1" element={React.createElement('h1',{},'sub1')}></Route>
              <Route path="sub2" element={React.createElement('h1',{},'sub2')}></Route>
        </Route>
</Routes>
}
// Parent组件定义
function Parent() {
  return (
    <div>
      <h2>Parent</h2>
      <Outlet/>
    </div>
  );
}

  1. 通过 matchRouteBranch 匹配的branches如下
// matches 匹配结果
parent -> parent/sub1
  1. 浏览器中地址栏如下
http://localhost:3000/parent/sub1
renderMatches 过程描述
  • 第一轮 循环得到的结果 记为 temp1
  1. match.route.element : sub1对应的element(即这里渲染sub1的h1元素)
  2. outlet: null
  3. matches和parentMatches : [parent的branch,sub1的branch]
<RouteContext.Provider
        value={{
          outlet:null,
          matches: [parent的branch,sub1的branch],
        }}
>
	{sub1}
</RouteContext.Provider>
  • 第二轮 循环得到的结果
  1. match.route.element : Parent组件对应的element
  2. outlet: 上一轮的结果 temp1
  3. matches和parentMatches : [parent的branch]
<RouteContext.Provider
        value={{
          outlet: <RouteContext.Provider
	        children={sub1}
	        value={{
	        outlet:null,
	        matches: [parent的branch,sub1的branch],
	        }}
		/>
          matches: [parent的branch],
        }}
>
	{Parent}
</RouteContext.Provider>
  1. <Parent> 中会消费 RouteContext(即这里得到的 temp1), 所以 sub1 就通过在<Parent>中的 <Outlet>进行了显示

其他

  • reduceRight中的 index 参数是倒序的