手搓简单前端路由

手搓简单前端路由

前言

在学习vue-router的时候我们就有了解过,前端路由的俩中模式:hash 和 history,那么这里我们就分别用这俩种方法来手写实现一个路由看看。

Hash 模式

在写之前,我们先了解一下以前 hash 是怎么用的。hash 可以用于页面内锚点定位,比如<a href="#title">title</a>,点击这个链接不会刷新页面,而是浏览器滚动到相应的位置。

其实 Hash 模式就是利用的这个特点,使得 SPA 应用控制路由时,不刷新页面,然后拦截 hash 的变化来切换页面。不过也带来了缺点,无法使用锚点定位了。

所以说我们的目的也就是俩个:

  1. 修改 url,并且同时需要保证不刷新页面
  2. 监听 url 变化,作出处理

在 hash 模式中,可以通过 a 标签来做到 1,浏览器也提供了onhashchange这个 API 来监听 hash 变化,所以我们可以写出如下代码

不过还有一个问题,刚进入页面时,没有内容,可以添加一个DOMContentLoaded事件处理就可以

<ul>
<li><a href="#/home">home</a></li>
<li><a href="#/about">about</a></li>
</ul>

<div id="routeView"></div>

<script>
const routes = [
{ path: '#/home', component: 'homePage' },
{ path: '#/about', component: 'aboutPage' },
]
const routerView = document.getElementById('routeView')

const router = () => {
const currentPath = window.location.hash
const route = routes.find(route => route.path === currentPath)
if (route) {
routerView.innerHTML = route.component
} else {
routerView.innerHTML = 'homePage'
}
}

window.addEventListener('DOMContentLoaded', router)
window.addEventListener('hashchange', router)
</script>

History 模式

当 a 链接的 href 属性里面不是 hash 时,会触发页面刷新,那么 history 该如何处理这个问题?以及他怎么监听 url 变化?

在 api 里面都提供了相应的功能,改变 url 可以使用pushState等,监听可以使用onpopstate。下面实现看看:

<ul>
<li><a href="/home">home</a></li>
<li><a href="/about">about</a></li>
</ul>

<div id="routeView"></div>

<script>
const routes = [
{ path: '/home', component: 'homePage' },
{ path: '/about', component: '<h1>aboutPage</h1>' },
]
const routerView = document.getElementById('routeView')

const router = () => {
const currentPath = window.location.pathname
const route = routes.find(route => route.path === currentPath)
if (route) {
routerView.innerHTML = route.component
} else {
routerView.innerHTML = 'homePage'
}
}

const initRoute = () => {
const links = document.querySelectorAll('a')
links.forEach(link => {
link.addEventListener('click', e => {
e.preventDefault()
window.history.pushState({}, '', e.target.href)
router()
})
})
}

window.addEventListener('DOMContentLoaded', initRoute)
window.onpopstate = router
</script>

不过他也有一些缺陷,在我们本地掩饰的时候,可能会发现,切换路由后刷新就有可能提示找不到资源,这其实也需要服务端配置路由。