프런트 엔드 라우팅이란 무엇입니까
소위 프런트 엔드 라우팅에는 클라이언트 브라우저가 서버.
프런트엔드 라우팅 존재의 합리성
Ajax의 칼이 드러나기 전, 프런트엔드가 아직 초기 단계에 있기 전에 라우팅 작업은 백엔드에 넘겨졌습니다. 페이지를 전환할 때 브라우저는 다른 <code><span style="font-size: 14px;">url</span>
请求;服务器接收到浏览器的请求时,通过解析不同的<code><span style="font-size: 14px;">url</span>去拼接需要的<span style="font-size: 14px;">html</span>
或者模板,然后将结果返回给浏览器端进行渲染。
服务器端路由也是不落俗套的有利亦有弊。它的好处是安全性更高,更严格得控制页面的展现。这在某些场景中是很有用的,譬如下单支付流程,每一步只有在上一步成功执行之后才能抵达。这在服务器端可以为每一步流程添加验证机制,只有验证通过才返回正确的页面。那么前端路由不能实现每一步的验证?自然不是,姑且相信你的代码可以写的很严谨,保证正常情况下流程不会错,但是另一个不得不面对的事实是:前端是毫无安全性可言的。用户可以肆意修改代码来进入不同的流程,你可能会为此添加不少的处理逻辑。相较之下,当然是后端控制页面的进入权限更为安全和简便。
另一方面,后端路由无疑增加了服务器端的负荷,并且需要<span style="font-size: 14px;">reload</span>
页面,用户体验其实不佳。
这样,前端路由就有用武之地了。首先,它的出现无疑减轻了服务器端的压力。特别是对于一个比较复杂的应用来讲,或者更确切的说,对于拥有一个复杂路由系统的应用来说,服务器端需要为每一个不同的url执行一段处理逻辑在高并发的情况下实在有点不堪重负;其次,页面的切换可以不需要刷新整个页面了,没有网络延迟,没有闪烁刷新,提升了用户体验。
前端路由实现方式
既然目标实现,我们需要解决的问题有哪些?我们可以将问题拆的稍微细一点,先制定一个亿的小计划,实现之后再进行下一步:)
在页面不刷新的前提下实现<code><span style="font-size: 14px;">url</span>变化
捕捉到<code><span style="font-size: 14px;">url</span>的变化,以便执行页面替换逻辑
如何实现更新<code><span style="font-size: 14px;">url</span>并且页面不刷新
正如前面所说,前端路由相较于后端路由的一个特点就是页面在不完全刷新的情况下进行视图的切换。页面<code><span style="font-size: 14px;">url</span>变了,但是并没有重新加载!看上去似乎有点不可思议,其实也没什么大不了。
试想将浏览器地址栏当做一个输入框,我们需要实现的就是改变输入框的<span style="font-size: 14px;">value</span>
但是不触发请求页面的操作,这样就不会重新加载新页面。倘若输入框的值的变化和发送请求是一个原子操作,我们也就束手无策了。庆幸的是,只有当我们敲击了回车之后,请求才会被发送出去(这是显而易见的吧)。因此这就为我们修改地址栏的值而不触发页面请求刷新创造了条件。BOM是否有提供修改浏览器地址栏<code><span style="font-size: 14px;">url</span>url 요청을 보냅니다. 서버가 브라우저의 요청을 받으면 다른
🎜url🎜
을 구문 분석합니다. 🎜html🎜
🎜 또는 템플릿을 검색한 다음 렌더링을 위해 결과를 브라우저에 반환합니다. 🎜🎜🎜🎜 서버측 라우팅 역시 틀에 얽매이지 않으며 장점과 단점이 있습니다. 장점은 보안이 강화되고 페이지 표시에 대한 제어가 엄격해진다는 것입니다. 이는 이전 단계가 성공적으로 실행된 후에만 각 단계에 도달할 수 있는 주문 결제 프로세스와 같은 특정 시나리오에서 유용합니다. 이를 통해 서버 측 프로세스의 각 단계에 확인 메커니즘을 추가할 수 있으며, 확인이 통과되면 올바른 페이지만 반환됩니다. 그렇다면 프런트 엔드 라우팅은 모든 단계에서 검증을 구현할 수 없습니까? 물론 그렇지 않습니다. 일반적인 상황에서 프로세스가 잘못되지 않도록 코드를 매우 엄격하게 작성할 수 있다고 생각합니다. 그러나 직면해야 할 또 다른 사실은 프런트 엔드에 보안이 전혀 없다는 것입니다. 사용자는 코드를 자유롭게 수정하여 다양한 프로세스에 들어갈 수 있으며 이를 위해 많은 처리 로직을 추가할 수 있습니다. 물론 이에 비해 백엔드 제어 페이지에 대한 액세스 권한이 더 안전하고 간단합니다. 🎜🎜🎜🎜반면에 백엔드 라우팅은 의심할 바 없이 서버 측의 부하를 증가시키며 🎜🎜reload🎜
🎜 페이지를 요구합니다. 실제로는 사용자 경험이 좋지 않습니다. 🎜🎜🎜🎜이러한 방식으로 프런트 엔드 라우팅이 작동합니다. 우선, 이 기능의 등장은 의심할 여지 없이 서버 측의 부담을 줄여줍니다. 특히 더 복잡한 애플리케이션, 더 정확하게 말하면 복잡한 라우팅 시스템이 있는 애플리케이션의 경우 서버는 각각의 다른 URL에 대해 처리 로직을 실행해야 하는데, 이는 동시성이 높은 상황에서는 약간 견딜 수 없습니다. 페이지를 전환할 때 전체 페이지를 새로 고칠 필요가 없으며, 네트워크 지연이나 깜박이는 새로 고침이 없어 사용자 경험이 향상됩니다. 🎜🎜🎜🎜프런트 엔드 라우팅 구현🎜🎜🎜🎜이제 목표가 달성되었으니 해결해야 할 문제는 무엇인가요? 문제를 조금 세분화해서 먼저 1억이라는 소소한 계획을 세우고, 깨달은 뒤 다음 단계로 넘어가면 됩니다 :)🎜🎜- 🎜 🎜페이지가 새로 고쳐지지 않습니다. 전제하에 🎜
🎜url🎜
🎜changes🎜🎜 - 🎜🎜는 🎜
🎜url🎜
의 변경 사항을 캡처합니다. 🎜 페이지 교체 로직을 실행하기 위해🎜 🎜
🎜업데이트 방법🎜🎜url🎜
🎜페이지가 새로 고쳐지지 않습니다🎜
🎜 🎜앞서 언급했듯이 프런트엔드 라우팅은 백엔드 라우팅과 비교됩니다. 터미널 라우팅의 한 가지 특징은 페이지를 완전히 새로 고치지 않고도 보기를 전환할 수 있다는 것입니다. 🎜🎜url🎜
🎜 페이지가 변경되었지만 새로고침되지 않았습니다! 조금 이상하게 보일 수도 있지만 사실 별거 아닙니다. 🎜🎜🎜🎜브라우저 주소 표시줄을 입력 상자로 처리한다고 상상해 보세요. 입력 상자의 🎜🎜value🎜
🎜를 변경하되 페이지 요청 작업을 실행하지 않는 것입니다. 새 페이지는 다시 로드되지 않습니다. 입력 상자의 값을 변경하고 요청을 보내는 것이 원자성 작업이라면 우리는 무력할 것입니다. 다행스럽게도 Enter 키를 누를 때까지는 요청이 전송되지 않습니다(이것은 명백합니다). 따라서 이는 페이지 요청 새로 고침을 트리거하지 않고 주소 표시줄의 값을 수정할 수 있는 조건을 만듭니다. BOM은 요청 작업을 트리거하지 않고 브라우저 주소 표시줄 🎜🎜url🎜
🎜을 수정하는 방법을 제공합니까? 🎜🎜여기서 요구 사항을 충족하는 방법에는 두 가지가 있습니다. 하나는 <code><span style="font-size: 14px;">url</span>
中的<span style="font-size: 14px;">hash</span>
字段;二是使用HTML5提供的history API。
<span style="font-size: 14px;">hash</span>
方式
了解<span style="font-size: 14px;">http</span>
协议就会知道,<code><span style="font-size: 14px;">url</span>的组成部分有很多,譬如协议、主机名、资源路径、查询字段等等,其中包含一个称之为片段的部分,以<span style="font-size: 14px;">#</span>
为标识。
例如: <span style="font-size: 14px;">http://www.gmail.com/text/#123</span>
,<span style="font-size: 14px;">123</span>
便是<code><span style="font-size: 14px;">url</span>中的<span style="font-size: 14px;">hash</span>
部分。
打开控制台,输入 <span style="font-size: 14px;">location.hash</span>
,你可以得到当前<code><span style="font-size: 14px;">url</span>的<span style="font-size: 14px;">hash</span>
部分(如果当前<code><span style="font-size: 14px;">url</span>不存在<span style="font-size: 14px;">hash</span>
则返回空字符串)。接下来,输入 <span style="font-size: 14px;">location.hash = '123'</span>
,会发现浏览器地址栏的<code><span style="font-size: 14px;">url</span>变了,末尾增加了<span style="font-size: 14px;">#123</span>
字段,并且,页面没有被重新刷新。很显然,这很符合我们的要求。
history API
HTML5引入了一个<span style="font-size: 14px;">history</span>
对象,包含了一套访问浏览器历史的API,可以通过<span style="font-size: 14px;">window.history</span>
访问到它。
这里我们看上了它的两个API方法:<span style="font-size: 14px;">pushState</span>
和 <span style="font-size: 14px;">replaceState</span>
。
<span style="font-size: 14px;">history.replaceState(dataObj, title, url);<br/>history.pushState(dataObj, title, url);<br/></span>
若上所示,它们接收完全相同的参数,都是对浏览器的历史栈进行操作,将传递的url和相关数据压栈,并将浏览器地址栏的<code><span style="font-size: 14px;">url</span>替换成传入的<code><span style="font-size: 14px;">url</span>且不刷新页面(正中下怀!)。
By the way,不同的地方是
<span style="font-size: 14px;">pushState</span>
将指定的<code><span style="font-size: 14px;">url</span>直接压入历史记录栈顶,而<span style="font-size: 14px;">replaceState</span>
是将当前历史记录栈顶替换成传入的数据。
这两种方式都可以帮我们满足题设条件。采用哪一种方式除了主观喜好之外,还得依照客观事实:低版本的浏览器对于history API的兼容性不好,例如遇到了IE8,摆在眼前的道路似乎就别无选择了。
如何跟踪<code><span style="font-size: 14px;">url</span>变化
在浏览器端,跟踪表单属性的变化一般都采用事件监听机制,跟踪<code><span style="font-size: 14px;">url</span>url의
🎜hash🎜
🎜 필드를 사용하는 것이고, 다른 하나는 🎜🎜HTML5에서 제공하는 기록 API🎜🎜를 사용하는 것입니다. 🎜🎜🎜hash🎜
🎜method🎜
🎜🎜 🎜🎜http🎜
🎜 프로토콜을 이해한다면, 🎜 🎜url🎜< /code>🎜에는 프로토콜, 호스트 이름, 리소스 경로, 쿼리 필드 등과 같은 많은 구성 요소가 있으며, 여기에는 🎜<code>🎜#🎜
🎜로 식별되는 조각이라는 부분이 포함됩니다. . 🎜🎜🎜🎜예: 🎜🎜http://www.gmail.com/text/#123🎜
🎜, 🎜🎜123🎜
🎜은 🎜 🎜url🎜
🎜의 🎜🎜hash🎜
🎜 부분. 🎜🎜🎜🎜콘솔을 열고 🎜🎜location.hash🎜
🎜를 입력하면 현재 🎜🎜url🎜
🎜의 🎜🎜hash🎜을 얻을 수 있습니다. </ code>🎜 부분(현재 🎜<code>🎜url🎜
🎜이 존재하지 않는 경우🎜🎜hash🎜
🎜, 빈 문자열이 반환됩니다). 다음으로 🎜🎜location.hash = '123'🎜
🎜를 입력하면 브라우저 주소 표시줄의 🎜🎜url🎜
🎜이 변경된 것을 확인할 수 있으며, 🎜 < 코드>🎜#123🎜🎜 필드가 추가되었으며 페이지가 새로 고쳐지지 않습니다. 분명히 이것은 우리의 요구 사항을 충족합니다. 🎜🎜🎜history API🎜
🎜🎜HTML5에서는 🎜🎜history🎜
🎜 객체를 도입했습니다. 여기에는 🎜<를 통해 액세스할 수 있는 브라우저 기록에 액세스하기 위한 API 세트가 포함되어 있습니다. code> 🎜window.history🎜🎜액세스하세요. 🎜🎜🎜🎜여기서 우리는 두 가지 API 메소드인 🎜🎜pushState🎜
🎜 및 🎜🎜replaceState🎜
🎜에 관심이 있습니다. 🎜🎜<span style="font-size: 14px;">var oldHash = location.hash;var oldURL = location.href;<br/><br/>setInterval(function() { var newHash = location.hash; var newURL = location.href; if (newHash !== oldHash && typeof window.onhashchange === 'function') { // 执行onhashchange回调<br/> window.onhashchange({ 'type': 'hashchange', 'oldURL': oldURL, 'newURL': newURL<br/> });<br/><br/> oldHash = newHash;<br/> oldURL = newURL;<br/> }<br/>}, 100);<br/></span>
🎜를 푸시합니다. url🎜
🎜은 페이지를 새로 고치지 않고 들어오는 🎜🎜url🎜
🎜으로 대체됩니다(원하는 대로!). 🎜🎜🎜🎜그런데 차이점은 🎜🎜🎜두 가지 방법 모두 질문 설정 조건을 충족하는 데 도움이 될 수 있습니다. 주관적인 선호 외에도 어떤 방법을 채택할지는 객관적인 사실에 기초해야 합니다. 낮은 버전의 브라우저는 히스토리 API와의 호환성이 좋지 않습니다. 예를 들어 IE8을 접할 경우 앞으로는 선택의 여지가 없는 것 같습니다. 🎜🎜🎜pushState🎜
🎜가 지정된 🎜🎜url🎜
🎜을 기록의 맨 위에 직접 푸시한다는 것입니다. stack, 그리고 🎜🎜replaceState🎜
🎜는 현재 기록 스택의 맨 위를 들어오는 데이터로 바꾸는 것입니다. 🎜🎜
🎜추적 방법🎜🎜url🎜
🎜changes🎜
🎜🎜브라우저 측에서 양식 속성의 변경 사항을 추적하는 것은 일반적으로 이벤트 수신 메커니즘을 사용하여 추적합니다.🎜 🎜url🎜
🎜의 변경 사항도 파격적입니다. 🎜🎜对于<span style="font-size: 14px;">hash</span>
方式的前端路由,通常可以监听 <span style="font-size: 14px;">hashchange</span>
事件,在事件回调中处理相应的页面视图展示等逻辑。
此外,HTML5提供的 <span style="font-size: 14px;">popstate</span>
事件也会在<code><span style="font-size: 14px;">url</span>的<span style="font-size: 14px;">hash</span>
发生改变时触发。也就是说如果可以忽略低版本浏览器,我们使用<span style="font-size: 14px;">hash</span>
方式路由时也可以采用监听这个事件进行回调处理。
那么,如果是采用history API的形式呢?根据MDN的描述:
调用
<span style="font-size: 14px;">history.pushState()</span>
或者<span style="font-size: 14px;">history.replaceState()</span>
不会触发<span style="font-size: 14px;">popstate</span>
事件。<span style="font-size: 14px;">popstate</span>
事件只会在浏览器某些行为下触发, 比如点击后退按钮(或者在JavaScript中调用<span style="font-size: 14px;">history.back()</span>
方法)。
这也就是说,我们在使用history API改变浏览器的<code><span style="font-size: 14px;">url</span>时,仍需要额外的步骤去触发 <span style="font-size: 14px;">popstate</span>
事件,例如调用 <span style="font-size: 14px;">history.back()</span>
会 <span style="font-size: 14px;">history.forward()</span>
等方法。
从兼容性上来讲,前面有提及<span style="font-size: 14px;">hash</span>
的方式兼容性更好。然而,对于低版本的浏览器,例如IE6等等,不支持 <span style="font-size: 14px;">hashchange</span>
事件。这个时候我们只能通过 <span style="font-size: 14px;">setInterval</span>
设置心跳的方式去模拟 <span style="font-size: 14px;">hashchange</span>
。
<span style="font-size: 14px;">var oldHash = location.hash;var oldURL = location.href;<br/><br/>setInterval(function() { var newHash = location.hash; var newURL = location.href; if (newHash !== oldHash && typeof window.onhashchange === 'function') { // 执行onhashchange回调<br/> window.onhashchange({ 'type': 'hashchange', 'oldURL': oldURL, 'newURL': newURL<br/> });<br/><br/> oldHash = newHash;<br/> oldURL = newURL;<br/> }<br/>}, 100);<br/></span>
一个简单实现
这里,给出一个很简单的实现:
<span style="font-size: 14px;">router.js</span>
<span style="font-size: 14px;">function FrontRouter() { this.routes = {};<br/> window.addEventListener('load', this.resolve.bind(this), false);<br/> window.addEventListener('hashchange', this.resolve.bind(this), false);<br/>}<br/><br/>FrontRouter.prototype.route = function(path, callback) { this.routes[path] = callback || function() {};<br/>};<br/><br/>FrontRouter.prototype.resolve = function() { this.curHash = location.hash.slice(1) || '/'; typeof this.routes[this.curHash] === 'function' && this.routes[this.curHash]();<br/>};<br/></span>
<span style="font-size: 14px;">index.html</span>
<span style="font-size: 14px;"><ul> <li><a href='#blue'></a></li> <li><a href='#yellow'></a></li> <li><a href='#red'></a></li></ul><br/></span>
<span style="font-size: 14px;">index.js</span>
<span style="font-size: 14px;">var router = new FrontRouter();<br/><br/>router.route('blue', function() {<br/> document.body.style.backgroundColor = 'blue';<br/>});<br/><br/>router.route('yellow', function() {<br/> document.body.style.backgroundColor = 'yellow';<br/>});<br/><br/>router.route('red', function() {<br/> document.body.style.backgroundColor = 'red';<br/>});<br/></span>
一点总结
应用场景
前端路由大部分的应用场景,就是我们现在熟知的单页应用SPA。
不存在纯前端路由
我们此前所描述的前端路由,建立在已经打开了一个初始页面基础之上,然后在这个页面之内进行页面替换。然而,我们如何进入这个初始页面?仅靠前端路由肯定是力所不及。我们至少要向后端发送一次http请求,接收所需要加载的页面不是吗?
所以,我们并不能抛弃后端路由部分。这也意味着,我们需要和后端确认各自的分工,哪些url归前端解析,哪些归后台解析。