フロントエンドルーティングとは
いわゆるフロントエンドルーティングには、クライアントブラウザが、サーバー。
フロントエンドルーティングの存在合理性
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🎜
🎜 またはテンプレートを使用して、結果をレンダリングのためにブラウザーに返します。 🎜🎜🎜🎜 サーバー側のルーティングも型破りで、長所と短所があります。その利点は、セキュリティが高く、ページ表示をより厳密に制御できることです。これは、注文の支払いプロセスなど、前のステップが正常に実行された後でのみ各ステップに到達できる特定のシナリオで役立ちます。これにより、サーバー側のプロセスの各ステップに検証メカニズムを追加でき、検証に合格した場合は正しいページのみが返されます。では、フロントエンドルーティングではすべてのステップで検証を実装することはできないのでしょうか?もちろん、そうではありません。通常の状況ではプロセスが間違っていないことを保証するためにコードを非常に厳密に記述することができると思いますが、直面しなければならないもう 1 つの事実は、フロントエンドにはセキュリティがまったくないということです。ユーザーはコードを自由に変更してさまざまなプロセスに入ることができ、そのために多くの処理ロジックを追加することもできます。もちろん、それに比べて、バックエンド制御ページへのアクセス許可はより安全かつ簡単です。 🎜🎜🎜🎜その一方で、バックエンドルーティングは間違いなくサーバー側の負荷を増加させ、🎜🎜reload🎜
🎜ページを必要とします。ユーザーエクスペリエンスは実際には良くありません。 🎜🎜🎜🎜このようにして、フロントエンドルーティングが機能します。まず第一に、その出現によりサーバー側の負担が軽減されることは間違いありません。特に、より複雑なアプリケーション、より正確に言えば、複雑なルーティング システムを備えたアプリケーションの場合、サーバーは異なる URL ごとに処理ロジックを実行する必要がありますが、これは同時実行性が高い状況では少々耐え難いものです。ページを切り替えるときにページ全体を更新する必要がなく、ネットワークの遅延や更新のちらつきがないため、ユーザー エクスペリエンスが向上します。 🎜🎜🎜🎜フロントエンドルーティングの実装🎜🎜🎜🎜目標は達成されたので、解決する必要がある問題は何でしょうか?問題を少し分解して、最初に 1 億という小さな計画を立て、それを理解した後に次のステップに進むことができます:)🎜🎜- 🎜 🎜ページが更新されない 前提として、🎜
🎜url🎜
🎜変更🎜🎜 - 🎜🎜は🎜
🎜url🎜
の変更をキャプチャします。 🎜ページ置換ロジックを実行するには🎜 🎜
🎜更新方法🎜🎜url🎜
🎜を実行してもページが更新されません🎜
🎜 🎜前に述べたように、フロントエンド ルーティングはバックエンド ルーティングと比較されます。ターミナル ルーティングの特徴の 1 つは、ページを完全に更新せずにビューを切り替えることができることです。ページ 🎜🎜url🎜
🎜 が変更されましたが、リロードされていません。少し奇妙に思えるかもしれませんが、実際には大したことではありません。 🎜🎜🎜🎜ブラウザのアドレス バーを入力ボックスとして扱うことを想像してください。達成する必要があるのは、ページをリクエストする操作をトリガーせずに、入力ボックスの 🎜🎜value🎜
🎜 を変更することです。新しいページはリロードされません。入力ボックスの値の変更とリクエストの送信がアトミックな操作である場合、私たちは無力になります。幸いなことに、Enter キーを押すまでリクエストは送信されません (これは明らかです)。したがって、これにより、ページ要求の更新をトリガーせずにアドレス バーの値を変更できる条件が作成されます。 BOM は、リクエスト操作をトリガーせずにブラウザーのアドレス バー 🎜🎜url🎜
🎜 を変更する方法を提供しますか? 🎜🎜ここで、ニーズを満たすには 2 つの方法があります。 1 つは、<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🎜
🎜 フィールドを使用する方法で、もう 1 つは、🎜🎜HTML5 によって提供される履歴 API🎜🎜 を使用する方法です。 🎜🎜
🎜ハッシュ🎜
🎜メソッド🎜
🎜🎜🎜🎜http🎜
🎜プロトコルを理解していれば、🎜がわかるでしょう。 🎜url🎜
🎜には、プロトコル、ホスト名、リソースパス、クエリフィールドなどの多くのコンポーネントがあり、🎜🎜#🎜
🎜で識別されるフラグメントと呼ばれる部分が含まれています。 。 🎜🎜🎜🎜例: 🎜🎜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 が含まれており、🎜 🎜window.history🎜🎜 にアクセスします。 🎜🎜🎜🎜ここでは、その 2 つの 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🎜
🎜 は、ページを更新せずに、受信した 🎜🎜url🎜
🎜 に置き換えられます (まさに必要なものです!)。 🎜🎜🎜🎜ちなみに、違いは、🎜🎜🎜 どちらの方法も、質問設定条件を満たすのに役立ちます。どの方法を採用するかは、主観的な好みに加えて、客観的な事実にも基づいている必要があります。たとえば、以前のバージョンのブラウザは履歴 API との互換性が低く、IE8 に遭遇した場合、選択の余地はないようです。 🎜🎜🎜pushState🎜
🎜が指定された🎜🎜url🎜
🎜を履歴の先頭に直接プッシュすることです。 stack、そして 🎜🎜replaceState🎜
🎜 は、現在の履歴スタックの先頭を受信データで置き換えることです。 🎜🎜
🎜追跡方法🎜🎜url🎜
🎜変更🎜
🎜🎜ブラウザ側でフォーム属性の変更を追跡するには、通常、イベントリスニングメカニズムを使用して追跡します🎜 🎜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归前端解析,哪些归后台解析。