前端实现路由
目的:
后端实现的路由本质是通过服务器代理与路径的配合去访问服务器上的静态资源。
而我们现在的项目都是单页面,路由控制权都是放在前端了。
前端框架现在都是单页面模式,了解他们实现的本质变得重中之重。
简单分析:
前端实现路由有两种方式 hash 与 history
- hash路由:
hash原本是作为页面定位使用,本身会在URL后携带#,切换路由只改变#后面的值,他不会改变浏览器访问服务器资源的地址(#前的才会被用在请求中),所以改变hash值就像我们改变url参数一样不会导致页面的重载
- history路由:
history路由直接会更改url,没有任何特殊符号参杂,浏览器会识别为访问服务器具体的路径造成访问404现象,所以我们一般会配置404重定向到index页面,然后交由index页面处理
常用框架中两种实现方式的共同点:
思考:比如history模式上线我们都要配置服务端,请求不到路径就走404,那每次路由跳转应该都会404,走服务端的重定向,network里都会有请求index.html才对,,而实际却没有,这是为什么?
其实无论hash还是history内部都是除了首次加载路由完全脱离了浏览器控制,后续的页面内的路由跳转事件都是前端处理的,浏览器的地址栏就相当于改了个样式,并不会实际发送请求到后端,也就不会经过代理服务器的的重定向。
框架中就是转化我们的路由配置,如果是内部跳转,根据我们跳转的地址,改变相应的url的显示,内部监听这次转变,对相应模块片段进行载入与卸载。如果是刷新或者浏览器回车式加载等,就是访问服务器上的index页面,再根据当前的url载入相应的片段
hash路由的实现
我们只需要调用location.hash就可以设置当前路由的hash值,接下来就是要监听了,有两种监听方式
(_=>{
location.hash = 'index'
})()
1. window.onhashchange = (e) => {}
2. window.addEventListener('hashchange',()=>{})
然后就可以在方法中执行页面的加载卸载
history路由的实现
history 模式依靠调用 history.pushState 方法以及监听 popstate 事件
pushState将url放入历史记录,显示在路由中,replaceState将url替换当前的历史记录,都不会向后端发起请求。
pushState 的时候,并不会触发 popstate 事件, popstate只会监听前进后退时候的变化,所以pushstate后就要根据url处理组件的逻辑,返回前进可以利用onpopstate来处理
(_=>{
history.pushState({name:'123'}, null, "index")
...// 首次组件处理
})()
1. window.onpopstate = (e) => {
...// 前进后退组件处理
}
2. window.addEventListener('popstate',()=>{})
pushState接收三个参数,一是传递的参数,二是页面设置的标题,三是改变后的url
简单实现
最近在用vue3,于是用vue3简单实现一个history 的 route hooks,仅供参考
import routeConfig from './customPage'
import {ref} from 'vue'
// 简单实现个路由hook
export default function initroute():Array<any> {
const routeNameArr = Object.keys(routeConfig)
const activeRoute = ref(routeNameArr[0])
// 返回当前激活的路由名
const getComponent=()=>{
if (window.location.pathname!=='/') {
activeRoute.value = window.location.pathname.split('/')[1]
// 找不到路由时
if (!routeNameArr.includes(activeRoute.value)){
activeRoute.value = routeNameArr[0]
}
}
routeChange(activeRoute.value)
// 前进后退处理
window.addEventListener('popstate',(e)=>{
if(e.state) {
activeRoute.value = e.state.routeName
}
})
return activeRoute
}
const routeChange=(routeName: string)=>{
history.pushState({routeName}, '', routeName)
activeRoute.value=routeName
}
return [
routeConfig,
getComponent,
routeChange
]
}
// 渲染组件
<component :key="freshNum" :is="activeName"></component>
const [ routeConfig, getComponent, routeChange ] = initroute()
const activeName = getComponent() // 当前选中组件名
const menuList: string[] = Object.keys(routeConfig) // 路由菜单
routeChange() //路由跳转执行
总结
两种方式虽然有些不同,但是实现理念与效果差距并不大实际上的使用差距并不大。
前端路由的核心原理并不难,继续往下要有很多细节的处理,理解了hash 和 history 两种模式,可以帮我们更好的理解vue/react路由的运作原理