来,手写一个简单的路由(一)

301 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前言

我们在之前的篇章中介绍了路由的一些问题,而当我们处理这些问题,追根到底还需要对其实现原理熟悉才能基于需求与目标实现修改。

今天我们主要的是实现一个简单的路由,我们不必听到路由就退怯,而本质上路由就是一个if-else或switch判断。

模式

在react-router中,主要分为三种模式,hash、history、memory,前二者基于浏览器的地址栈模型,后者则是在内部实现自己的地址栈来维护。

本质上他们都是基于事件监听-响应处理:

image.png

实现

工程结构

//App入口
export default function App() {
  return (
    <div>
        //做点什么
    </div>
  );
}

//首页
export default function Index() {
    return (
        <h1>Index</h1>
    )
}

//home页面
const Home = () => {
    return <>
        <h1>Home</h1>
    </>
}
export default Home

//about页面
const About = () => {
    return <>
       <h1>about</h1>
    </>
}
export default About

//users页面
function Users() {
  return <>
    <h1>User</h1>
  </>
}
export default Users

不同于传统的路由配置,我们基于配置列表,这里借鉴了常见的vue-router结构,我们先申明自己的配置列表:

const route = [
  {
    component: <Index />,
    pathname: "/"
  },
  {
    component: <Home />,
    pathname: "/home"
  },
  {
    component: <About />,
    pathname: "/about"
  },
  {
    component: <Users />,
    pathname: "/users"
  }
]

针对配置列表,我们写一个route容器包裹并接受配置:

function MyRouter(props) {
  //基于window的location,我们匹配当前的符合选项
  //实际上在react-router是通过switch去匹配符合项,我们也可以通过是否精确匹配处理匹配规则还有地址栏参数等
  const { pathname } = window.location
  const Comp = props.route.find(item => item.pathname === pathname)

  return <>
    {Comp.component}
  </>
}

我们需要知道,组件即对象,而我们的配置列表之所以用标签包裹起来,则是告诉react让其把该对象当做组件去处理。

通过上面的简单路由,我们实现了组件与地址栏的匹配:

image.png

image.png

image.png

image.png

现在的问题是我们跳转时通过在地址栏输入而改变的,如果我们用a标签跳转,结果会是页面刷新了,这不是我们想要的我刷新跳转的模式,我们要实现的是基于history去实现跳转。

所以我们可以通过该api去实现自己的Link组件:

function MyLink(props) {
  const handleLinkClick = () => {
        window.history.pushState({},"",props.to)
  }
  return <>
    <span onClick={handleLinkClick}>  {props.children}</span>
  </>
}

我们在主页使用该组件:

export default function App() {
  return (
    <div>
      <div>
        <MyLink to="/">/</MyLink>
        <MyLink to="/home">home</MyLink>
        <MyLink to="/about">about</MyLink>
        <MyLink to="/users">users</MyLink>
      </div>
      <MyRouter route={route}>
      </MyRouter>
    </div>
  );
}

image.png

image.png

image.png

image.png

上面的模型只有当我们手动刷新时去触发状态更新才能导致组件重新匹配规则,所以后续我们要做的是监听地址变更以及手动push的操作,然后去setState一下即可。

小结

本篇章我们主要讲解的是路由的基本模型,触发响应的模型,而路由本质只是简单的匹配,组件即对象。所谓的切换也不过是视觉上的切换,除了节点的增删,我们用样式上的显示隐藏其实也是一样的。diff算法也无非是两个对象的比较寻找dom操作最少的最优解。