"Me inscribí para participar en el primer desafío del Proyecto Golden Stone: para compartir el premio acumulado de 100,000, este es mi primer artículo, haga clic para ver los detalles del evento "
Se acerca el reclutamiento de otoño. Al solicitar el puesto de front-end, creo que el entrevistador ha preguntado a muchos estudiantes sobre el enrutamiento de front-end y su método de implementación. Después del estudio, el autor hablará brevemente sobre sus puntos de vista y comprensión de los problemas anteriores.
prefacio
Cuando se trata de enrutamiento, tenemos que mencionar la aplicación de página única SPA, es decir, todo el proyecto tiene solo un archivo html, y todas las conversiones de datos de página y el salto de contenido se implementan mediante enrutamiento. Es decir, haga coincidir diferentes rutas de URL, analícelas y luego represente dinámicamente el contenido html regional.
Enrutamiento de front-end
Describa el mapeo entre la URL y la interfaz de usuario (página), llamémoslo 前端路由
, pertenecer 单向映射
, el cambio de URL hace que la interfaz de usuario cambie (presente contenido html diferente).
Cómo implementar el enrutamiento front-end
- ¿Cómo puedo detectar que la url ha cambiado?
- ¿Cómo puedo cambiar la URL sin que la página se actualice?
Modo de implementación de enrutamiento front-end
Para la mayoría de los proyectos actuales, son proyectos SPA, cuando el proyecto es un poco más complicado, se requiere enrutamiento de front-end, y en los dos marcos de front-end más populares de vue, vue-router es el enrutamiento estándar de vue y hay dos modos en él: hash
y history
.
Analicemos en detalle cómo los dos modos implementan el enrutamiento frontal.
modo hash
En primer lugar, debemos saber que el valor hash del navegador (hash) significa que url 后面的#号以及后面的字符
el cambio del valor hash no hará que el navegador envíe una solicitud al servidor, y el cambio del valor hash activará el evento onhashchange. Aunque el hash aparece en la URL, no se incluirá en la solicitud HTTP y no tiene ningún efecto en el backend, por lo que cambiar el hash no recargará la página.
El núcleo de implementación de hash:
1. El cambio del valor hash del navegador no hará que el navegador envíe una solicitud al servidor y no actualizará la página de la interfaz de usuario.
2. Use el método propio del navegador hashchange
para monitorear los cambios del hash del navegador.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li><a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>
<!-- 渲染对应的ui -->
<div id="routerView"></div>
<script>
let routerView = document.getElementById('routerView'); // 获取插入html的dom结构
window.addEventListener('load',onHashchange)
window.addEventListener('hashchange', onHashchange)// 浏览器自带的监听哈希值改变的方法:hashchange
// 控制渲染对应的 UI
function onHashchange() {
// console.log(location.hash);
switch (location.hash) { // location.hash为哈希值
case '#/home':
routerView.innerHTML = 'Home'
return
case '#/about':
routerView.innerHTML = 'About'
return
default:
return
}
}
routerView.innerHTML = 'Home'
</script>
</body>
</html>
复制代码
load
事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。先记录一次浏览器的url,当点击两个a标签中任意一个都可以触发hashchange
事件,而我们写的onHashchange函数,就是模拟页面匹配 url 去渲染相应的 HTML 内容,例子中我们使用的是向一个div容器中插入html内容 。
- hash模式所有的操作都是在前端完成的,不需要向后端(服务器)发出请求。
- 通过监听URL中hash部分的变化,从而做出对应的渲染逻辑。
- 缺点是url中带有 '#' 影响美观。
简单了解了hash模式的原理及实现方式,我们来聊聊history模式的原理及实现方式。
history模式
history模式的实现主要是因为HTML5中提供的history全局对象中的一些方法,比如:
window.history.go(pageNum)
可以跳转到浏览器会话历史中的指定的某一个记录页window.history.forward()
指向浏览器会话历史中的下一页,跟浏览器的前进按钮相同window.history.back()
返回浏览器会话历史中的上一页,跟浏览器的回退按钮功能相同window.history.pushState(stateData, title, url)
可以将给定的数据压入到浏览器会话历史栈中window.history.replaceState(stateData, title, url)
将当前的会话页面的url替换成指定的数据
而history模式的核心就是利用了 window.history.pushState(stateData, title, url)
和 window.history.replaceState(stateData, title, url)
,因为这两种方法用于修改 url 时,不会刷新页面。
pushState是压入浏览器的会话历史栈中,会使得history.length加1,而replaceState是替换当前的这条会话历史,因此不会增加history.length。
第一个问题:如何改变 url 却不引起页面的刷新?history模式使用以上两种方法就可以实现,但是第二个问题,如何监听 url 发生改变?
其实官方提供了一个事件 Window: popstate event
,当用户导航会话历史记录时活动历史记录条目发生更改时,会触发[Window
](Window: popstate event - Web APIs | MDN (mozilla.org)) 界面的 popstate
事件。
但是官方也说明了
其中关键就是仅调用 history.pushState
或者 history.replaceState
方法时,不会触发popstate
事件,但是单击浏览器前进或后退按钮会触发popstate
事件(或者在js中调用history.forward()
或者history.back()
)
意思就是不能用 popstate 来监听 history.forward()
或者history.back()
在笔者查阅文章资料的时候发现了,我们可以重写 history.forward()
和history.back()
两个方法,使其暴露在window全局,这样我们就可以监听重写的两个方法了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li><a href="/home">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
<!-- 渲染对应的UI -->
<div id="routerView"></div>
<script>
let routerView = document.getElementById('routerView')
window.addEventListener('DOMContentLoaded', onLoad)
// window.addEventListener('popstate', onPopState) // 浏览器的前进后退能匹配
function onLoad() {
onPopState()
let links = document.querySelectorAll('li a[href]')
// 拦截a标签的默认跳转行为
links.forEach(a => {
a.addEventListener('click', (e) => {
e.preventDefault() // 阻止a标签的href行为
history.pushState(null, '', a.getAttribute('href')) // 跳转
onPopState()
})
})
}
function onPopState() {
// console.log(location.pathname);
switch (location.pathname) {
case '/home':
routerView.innerHTML = '<h2>home page</h2>'
return
case '/about':
routerView.innerHTML = '<h2>about page</h2>'
return
default:
return
}
}
</script>
</body>
</html>
复制代码
笔者的例子中是采用了preventDefault()
阻止a
标签的默认href跳转行为,再利用document.querySelectorAll获取到a
标签上的href属性,在此基础上再监听a
标签的点击事件,在点击时间内,利用history.pushState
或者 history.replaceState
方法来实现模拟路由跳转。想监听history.pushState
或者 history.replaceState
可参考 (# 面试官为啥总是喜欢问前端路由实现方式?)
history 致命的缺点就是当改变页面地址后,强制刷新浏览器时,(如果后端没有做准备的话)会报错,因为刷新是拿当前地址去请求服务器的,如果服务器中没有相应的响应,会出现 404 页面。
结语
Por lo tanto, la elección de nuestro enrutamiento de front-end depende de nuestros escenarios de uso, requisitos, etc. Ambos pueden implementarse, cada uno con sus propias ventajas y desventajas. Las anteriores son algunas de mis opiniones personales sobre el enrutamiento de front-end. Cualquier pregunta puede ser discutido en el área de comentarios, gracias ¡Gracias por mirar!