"골든스톤 프로젝트 1차 챌린지 참여 신청했습니다 - 상금 100,000 공유, 첫 글 입니다, 이벤트 상세보기 클릭 "
가을 모집이 다가오고 있습니다. 프론트엔드 지원시 많은 학생들이 면접관으로부터 프론트엔드 라우팅 및 구현 방법에 대해 질문을 받았을 거라 생각합니다. 연구 후 저자는 자신의 견해와 위의 문제에 대한 이해.
머리말
라우팅에 관해서는 SPA 단일 페이지 애플리케이션을 언급해야 합니다. 즉, 전체 프로젝트에는 하나의 html 파일만 있고 모든 페이지 데이터 변환 및 콘텐츠 점프는 라우팅에 의해 구현됩니다. 즉, 다른 URL 경로를 일치시키고 구문 분석한 다음 지역 HTML 콘텐츠를 동적으로 렌더링합니다.
프런트 엔드 라우팅
url과 UI(페이지) 사이의 매핑을 설명하고, , , 이라고 부르겠습니다 前端路由
. 单向映射
url 변경으로 인해 UI가 변경됩니다(다른 html 콘텐츠 렌더링).
프런트 엔드 라우팅을 구현하는 방법
- URL이 변경되었음을 어떻게 알 수 있습니까?
- 페이지를 새로 고치지 않고 URL을 변경하려면 어떻게 해야 합니까?
프런트 엔드 라우팅의 구현 모드
현재 프로젝트의 대부분은 SPA 프로젝트이며, 프로젝트가 조금 더 복잡할 때 프론트 엔드 라우팅이 필요하며 vue의 가장 인기있는 두 가지 프론트 엔드 프레임 워크에서 vue-router는 vue의 표준 라우팅입니다. hash
및 두 가지 모드가 있습니다 history
.
두 모드가 프런트 엔드 라우팅을 구현하는 방법을 자세히 분석해 보겠습니다.
해시 모드
먼저 브라우저의 해시 값(hash)은 url 后面的#号以及后面的字符
해시 값의 변경으로 인해 브라우저가 서버에 요청을 보내지 않고 해시 값의 변경으로 인해 onhashchange 이벤트가 발생한다는 것을 알아야 합니다. 해시는 URL에 나타나지만 HTTP 요청에 포함되지 않고 백엔드에 영향을 미치지 않으므로 해시를 변경해도 페이지가 다시 로드되지 않습니다.
해시의 구현 핵심:
1. 브라우저의 해시 값을 변경해도 브라우저가 서버에 요청을 보내지 않으며 UI 페이지를 새로 고치지 않습니다.
2. 브라우저 고유의 hashchange
방법을 사용하여 브라우저 해시의 변경 사항을 모니터링합니다.
<!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 页面。
结语
따라서 프론트 엔드 라우팅의 선택은 사용 시나리오, 요구 사항 등에 따라 달라집니다. 둘 다 구현될 수 있으며 각각 장단점이 있습니다. 위의 내용은 프론트 엔드 라우팅에 대한 개인적인 의견입니다. 질문이 있을 수 있습니다. 댓글 영역에서 논의할 수 있습니다. 시청해주셔서 감사합니다!