简易手写react-router(函数式组件)

自己实现一个react-router

期望最简单的使用效果

export default () => {
      return (
        <>
            <Link to="/a">菜单a</>
            <Link to="/b">菜单b</>
        </>
        <Router>
            <Route path="/a"><A /></Route>
            <Route path="/b"><B /></Route>
        </Router>
      )
}
Router一般分HashRouter和BrowserHistoryRouter,原理区别分别是对location.hash和history api的处理

先写一些公用组件
// Link
const Link = ({ children }) => <a>children</a>

// Route 
const Route = () => {}

实现HashRouter

原理是在Link上绑定点击更改hash值事件,利用浏览器监听hash值的变化做相应的动作(渲染相应组件),因为是函数组件,会用到useEffect,useState

// HashRouter
const HashRouter = ({ children }) => {
  const [hash, setHash] = useState(window.location.hash)
  // hash变更触发
  const hashChange = e => {
      const { newURL } = e
      setHash(newURL.slice(newURL.indexOf('#')))
  }
  // 记得销毁时要清除绑定
  useEffect(() => {
    window.addEventListener('hashchange', hashChange)
    return window.removeEventListener('hashchange', hashChange)
  }, [])
 // 为什么不用children遍历,因为children有3种情况(1. 没子组件会返回undefined 2.单个子组件返回一个对象 3.多个子组件返回数组),而React.Children会自行判断,比较安全,当然要多个null兜底,因为返回undefined也会报错
  return React.Children.map(children, child => {
      if(child.type.name === 'Route') {
        if(child.props.path === hash) {
            return child.props.children
        }
        return null
      }
      return child.props.children
  }) || null
}

相应Link也要添加点击逻辑

const Link = ({ to, children, handleClick }) => {
    const handleClick = () => window.location.hash = to
    <span onClick={handleClick}>{children}</span>
}
加一点排版

最简单的功能就这样完成了

...
<div id='menu'>
    <div><Link to='a'>a组件</Link></div<
</div>
<div id='container'>
    <HashRouter>
       <Route path='/a'><A /></Route>
    </HashRouter>
</div>

接着搞嵌套路由看看

export default () => <div style={containerStyle}>
  <div style={menuStyle}>
    <div><Link to='#/a'>a组件</Link></div>
    <div><Link to='#/b'>b组件</Link></div>
    <div><Link to='#/c'>C组件</Link></div>
  </div>
  <div style={contentStyle}>
    <HashRouter>
      <Route path="#/a"><A /></Route>
      <Route path="#/b"><B /></Route>
      <Route path="#/c"><C /></Route>
    </HashRouter>
  </div>

</div>

const C1 = () => <div>我是C1</div>
const C2 = () => <div>我是C2</div>

const C = () => (
  <div>
    <Link to='#/c/c1'>C1组件</Link>
    <Link to='#/c/c2'>C2组件</Link>
    <div style={contentStyle}>
      <h2>c下面的组件</h2>
      <HashRouter>
        <Route path="#/c/c1"><C1 /></Route>
        <Route path="#/c/c2"><C2 /></Route>
      </HashRouter>
    </div>
  </div>
)
#/c的效果
点击C1

点击C1或C2的Link,发现整个C都消失了,分析到因为当前hash应当是#/c/c1,而C组件本身对于要在#/c的时候才会渲染,用全等判断自然不会渲染出来,子组件C1和C2肯定也不会出来


hash全等

将全等改成includes,发现效果出来了

    if (child.type.name === 'Route') {
      if (hash.includes(child.props.path)) {
        return child.props.children
      }
      return null
    }
改动后

待续更新BrowserRouter

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • React Router教程 React项目的可用的路由库是React-Router,当然这也是官方支持的。它也分...
    IT老马阅读 58,928评论 0 49
  • React Router教程 React项目的可用的路由库是React-Router,当然这也是官方支持的。它也分...
    张十七y阅读 348评论 0 0
  • React Router教程 React项目的可用的路由库是React-Router,当然这也是官方支持的。它也分...
    __db84阅读 742评论 0 0
  • 1. 后端路由与前端路由 多页面应用和后端路由在传统的Web应用中,浏览器根据地址栏的URL向服务器发送一个HTT...
    ImmortalSummer阅读 983评论 0 1
  • React Router教程 React项目的可用的路由库是React-Router,当然这也是官方支持的。它也分...
    应晨皓阅读 454评论 0 0